पाठ 22 / 25

Spring Data JPA Repositories

Use repository interfaces, derived queries, @Query and paging.

Repositories without boilerplate

Spring Data JPA generates repository implementations from interfaces. Extending JpaRepository<Entity, Id> provides save, findById, findAll, deleteById, paging and sorting, and batch operations. Derived query methods build queries from method names: findByEmailIgnoreCase, findByStatusAndPlacedAtAfter, countByCategorySlug, existsBySku, findTop10ByOrderByPriceDesc. Return Optional<T> for single results that may be absent, List<T>, Stream<T> (inside a transaction) or Page<T>/Slice<T> with a Pageable argument. When names become unreadable, use @Query with JPQL or native SQL, and @Modifying for updates and deletes. save() calls persist for new entities and merge otherwise (deciding "new" by a null ID or version), so with assigned IDs it may perform an extra SELECT; implementing Persistable or using a version field avoids this. Keep repositories focused on one aggregate root, and keep business logic in services or domain objects rather than in repository default methods.

Interfaces become implementations

You declare repository interfaces; Spring Data generates the queries and implementation at start-up.

An outlined interface shape on the left and a solid filled shape on the right connected by an arrow passing through a gear.
Figure 8.1 — Spring Data generating a repository implementation.

A repository with derived queries, @Query and paging

Method names express simple queries; @Query handles the rest.

public interface CustomerRepository extends JpaRepository<Customer, Long> {

    Optional<Customer> findByEmailIgnoreCase(String email);

    boolean existsByEmailIgnoreCase(String email);

    Page<Customer> findByCityOrderByNameAsc(String city, Pageable pageable);

    List<Customer> findTop5ByOrderByLifetimeValueDesc();

    @Query("""
        select c from Customer c
        where c.lastOrderAt < :before and c.marketingOptIn = true
        """)
    List<Customer> dormantCustomers(Instant before);

    @Modifying
    @Query("update Customer c set c.marketingOptIn = false where c.id = :id")
    int optOut(Long id);
}

// usage
Page<Customer> page = customers.findByCityOrderByNameAsc("Pune", PageRequest.of(0, 25));
long total = page.getTotalElements();      // Page runs an extra COUNT query; Slice does not

Page versus Slice

Page runs an extra COUNT(*) query to know the total number of rows, which can be expensive on large tables. Use Slice (or keyset pagination) for infinite-scroll style lists that only need "is there a next page?".

त्वरित जाँच: What does a Spring Data method named findByStatusAndPlacedAtAfter(status, date) do?

  • It must be implemented manually
  • It loads every row and filters in Java
  • Spring Data derives a query filtering by status and placedAt greater than the given date
  • It creates a database index
Answer

Spring Data derives a query filtering by status and placedAt greater than the given date — Derived query methods are parsed from their names into JPQL.