Spring Data JPA

1 min readUpdated January 16, 2026springspring-bootjpadatabase

Spring Data JPA removes almost all of the boilerplate involved in talking to a relational database: define an entity, define a repository interface, and Spring generates the implementation for you at startup — built on the same autoconfiguration mechanism covered in Autoconfiguration.

Layered diagram: controller calls service, service calls repository, repository talks to the database
A typical Spring Boot request flow down to the database.

1. Define the entity

Product.java
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
// getters and setters omitted
}

2. Define the repository — just an interface

ProductRepository.java
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findByNameContainingIgnoreCase(String name);
List<Product> findByPriceLessThan(BigDecimal price);
}

Line 1 is the entire trick: extending JpaRepository<Product, Long> gives you save, findById, findAll, deleteById, and pagination — for free. The two extra methods above are derived queries — Spring parses the method name and generates the SQL automatically.

3. Use it from a service

ProductService.java
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public List<Product> searchByName(String name) {
return repository.findByNameContainingIgnoreCase(name);
}
}

Writing custom queries with @Query

When method-name derivation gets unwieldy, drop down to JPQL directly:

@Query("SELECT p FROM Product p WHERE p.price BETWEEN :min AND :max ORDER BY p.price ASC")
List<Product> findInPriceRange(@Param("min") BigDecimal min, @Param("max") BigDecimal max);

Pagination and sorting

Page<Product> page = repository.findAll(
PageRequest.of(0, 20, Sort.by("price").descending())
);

JpaRepository accepts a Pageable on almost every finder method, so pagination doesn’t require any extra plumbing in the repository itself.

Common pitfall: the N+1 query problem

Lazy-loaded associations (the JPA default for @OneToMany/@ManyToMany) can silently issue one query per row when you iterate a collection. Fix it with a fetch join:

@Query("SELECT p FROM Product p JOIN FETCH p.reviews WHERE p.id = :id")
Optional<Product> findWithReviews(@Param("id") Long id);