Spring Data JPA
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.
1. Define the entity
@Entitypublic 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
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
@Servicepublic 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);