← All articles
Backend1 min read

Spring Data JPA Without the Surprises

Repositories, derived queries, and the N+1 problem. How to use Spring Data JPA productively without hidden performance traps.

S

Swapnika Voora

Author

Spring Data JPA generates repository implementations for you at runtime. It's enormously productive, but a few defaults can bite you in production if you don't understand what's happening underneath.

Declaring a repository

Extend JpaRepository and you get CRUD operations for free. Derived query methods are parsed from the method name.

AuthorRepository.java
public interface AuthorRepository extends JpaRepository<Author, Long> {
 
    List<Author> findByLastName(String lastName);
 
    Optional<Author> findByEmail(String email);
 
    List<Author> findByActiveTrueOrderByCreatedAtDesc();
}

The N+1 problem

The most common JPA performance trap: loading a list of entities, then lazily fetching an association for each one — turning one query into N+1.

nplusone.java
// This triggers one query for authors,
// then one more per author for their books.
List<Author> authors = authorRepository.findAll();
authors.forEach(a -> a.getBooks().size()); // lazy load per author

Fix it with a fetch join so the association loads in a single query.

AuthorRepository.java
@Query("SELECT a FROM Author a LEFT JOIN FETCH a.books")
List<Author> findAllWithBooks();

Use projections for read models

When you only need a few columns, fetching full entities is wasteful. Interface projections let Spring build lightweight read models.

AuthorSummary.java
public interface AuthorSummary {
    Long getId();
    String getLastName();
}

Rules of thumb

  • Watch your SQL logs during development — enable show-sql.
  • Reach for fetch joins or @EntityGraph when you hit N+1.
  • Use projections for read-heavy endpoints.

JPA is powerful once you can see the queries it generates. Make them visible and the surprises disappear.

#java#spring#jpa#database

More in Backend