All Spring templates

Spring Data JPA

Repositories, derived queries, custom JPQL, projections, and N+1 prevention.

DevZone Tools2,040 copiesUpdated Mar 19, 2026SpringJava
# CLAUDE.md — Spring Data JPA

## Repositories

- Extend `JpaRepository<Entity, Id>` for the standard CRUD surface. `CrudRepository` is the lowest common denominator.
- Don't write methods you don't use. The auto-generated query is only created when called.
- One repository per aggregate root. Don't share a `UserRepository` across unrelated services.

## Derived queries

- Use derived query methods for simple cases: `findByEmail`, `findByStatusAndCreatedAtAfter`.
- When the method name gets longer than ~50 chars, switch to `@Query`.
- `@Query("...")` for explicit JPQL. Use named parameters (`:name`), not positional.
- `@Query(nativeQuery = true)` only when JPQL can't express it. Document why.

## Projections

- Read just the columns you need. Don't fetch full entities for a list view.
- Interface projections — Spring builds the proxy from getter signatures:
  ```java
  interface UserListView {
      String getId();
      String getEmail();
      Instant getCreatedAt();
  }
  List<UserListView> findAllProjectedBy();
  ```
- DTO projections in JPQL: `select new com.app.UserDto(u.id, u.email) from User u`.

## N+1 prevention

- `@EntityGraph` on repository methods to fetch associations eagerly when you need them:
  ```java
  @EntityGraph(attributePaths = {"orders", "address"})
  List<User> findAll();
  ```
- For collection associations, prefer `JOIN FETCH` in `@Query` over `EntityGraph` for control over the join type.
- Profile every list endpoint. **Hibernate Statistics** or **DataSource Proxy** in dev catch N+1s before prod.

## Pagination

- `Pageable` parameter on repository methods. Spring derives `LIMIT` / `OFFSET`.
- Cursor-based pagination via slice queries (`Slice<T> findFirst20ByOrderByIdAscIdGreaterThan(String cursor)`) for large datasets.
- Don't use `OFFSET` for deep pagination — performance degrades quadratically.

## Transactions

- `@Transactional` on service methods, not on repositories. Repositories run inside the caller's transaction.
- `readOnly = true` on queries. Hibernate skips dirty-checking and flush.
- Don't lazy-load attributes outside the transaction (`LazyInitializationException`). Either fetch them eagerly with `EntityGraph` or use a projection.

## Auditing

- `@CreatedDate`, `@LastModifiedDate`, `@CreatedBy`, `@LastModifiedBy` for free with `@EnableJpaAuditing`.
- One `AuditorAware` bean to expose the current user. Don't repeat in every entity.
- Add `@EntityListeners(AuditingEntityListener.class)` to the entity (or a `@MappedSuperclass`).

## Migrations

- **Flyway** or **Liquibase**. Don't rely on `spring.jpa.hibernate.ddl-auto=update` in production.
- `ddl-auto=validate` in production to assert the schema matches.
- Run migrations as a separate step before app boot in multi-instance deploys.

## Don't

- Don't use `findAll()` without pagination on tables that grow.
- Don't return `Optional<List<T>>` — use an empty list.
- Don't expose JPA entities through controllers. Map to DTOs at the boundary.
- Don't catch `EntityNotFoundException` in services to translate it. Use a `Optional` return or throw a domain exception.

Other Spring templates