# Spring Data JPA Repositories — Hibernate / JPA

Source: https://www.skillbyai.com/en/hibernate-jpa/p-spring-data

> 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.](assets/figures/hibernate-jpa/section-8-map.svg) — 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.

```java
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?".

**Quiz:** What does a Spring Data method named findByStatusAndPlacedAtAfter(status, date) do?

- [ ] It must be implemented manually
- [ ] It loads every row and filters in Java
- [x] 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.
