Spring Data JPA
Repositories, derived queries, custom JPQL, projections, and N+1 prevention.
# 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
Spring Framework Core Rules
Dependency injection, bean lifecycle, configuration classes, and profiles.
Spring Security Rules
SecurityFilterChain (no WebSecurityConfigurerAdapter), OAuth2 resource server, JWT.
Spring MVC Rules
Controllers, ResponseEntity, validation, exception handling, and content negotiation.
Spring WebFlux (Reactive)
Reactive Spring with Mono/Flux, R2DBC, backpressure, and non-blocking patterns.
Spring Boot 3 Modern Rules
Boot 3+ defaults: starters, configuration properties, profiles, and structured logging.