Spring Data JPA
Overview
Provides patterns for Spring Data JPA repositories, entity relationships, queries, pagination, auditing, and transactions.
When to Use
Creating repositories with CRUD operations, entity relationships, @Query annotations, pagination, auditing, or UUID primary keys.
Instructions
Create Repository Interfaces
To implement a repository interface:
-
Extend the appropriate repository interface:
@Repository public interface UserRepository extends JpaRepository<User, Long> { // Custom methods defined here } -
Use derived queries for simple conditions:
Optional<User> findByEmail(String email); List<User> findByStatusOrderByCreatedDateDesc(String status); -
Implement custom queries with
@Query:@Query("SELECT u FROM User u WHERE u.status = :status") List<User> findActiveUsers(@Param("status") String status);
Configure Entities
-
Define entities with proper annotations:
@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, length = 100) private String email; } -
Configure relationships using appropriate cascade types:
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<Order> orders = new ArrayList<>();Validation: Test cascade behavior with a small dataset before applying to production data. Verify delete operations don't cascade unexpectedly.
-
Set up database auditing:
@CreatedDate @Column(nullable = false, updatable = false) private LocalDateTime createdDate;
Apply Query Patterns
- Use derived queries for simple conditions
- Use
@Query for complex queries - Return Optional<T> for single results
- Use Pageable for pagination
- Apply
@Modifying for update/delete operations
Manage Transactions
- Mark read-only operations with
@Transactional(readOnly = true) - Use explicit transaction boundaries for modifying operations
- Specify rollback conditions when needed
Validate and Optimize
1. Verify entity configuration:
- Test cascade behavior in a transaction before production deployment
- Validate bidirectional relationships sync correctly
2. Optimize query performance:
- Run
EXPLAIN ANALYZEon queries against large tables - If performance issues detected: add indexes → verify with EXPLAIN → repeat
- Use
@EntityGraphto prevent N+1 queries
3. Validate pagination:
- Ensure indexed columns support pagination queries
- Test with large datasets to verify cursor stability
Examples
Basic CRUD Repository
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
// Derived query
List<Product> findByCategory(String category);
// Custom query
@Query("SELECT p FROM Product p WHERE p.price > :minPrice")
List<Product> findExpensiveProducts(@Param("minPrice") BigDecimal minPrice);
}
Pagination Implementation
@Service
public class ProductService {
private final ProductRepository repository;
public Page<Product> getProducts(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
return repository.findAll(pageable);
}
}
Entity with Auditing
@Entity
@EntityListeners(AuditingEntityListener.class)
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(nullable = false, updatable = false)
private String createdBy;
}
Best Practices
Entity Design
- Use constructor injection exclusively (never field injection)
- Prefer immutable fields with
finalmodifiers - Use Java records (16+) or
@Valuefor DTOs - Always provide proper
@Idand@GeneratedValueannotations - Use explicit
@Tableand@Columnannotations
Performance Optimization
- Use appropriate fetch strategies (LAZY vs EAGER)
- Implement pagination for large datasets
- Use database indexes for frequently queried fields
- Consider using
@EntityGraphto avoid N+1 query problems
Reference Documentation
For comprehensive examples, detailed patterns, and advanced configurations, see:
- Examples - Complete code examples for common scenarios
- Reference - Detailed patterns and advanced configurations
Constraints and Warnings
- Never expose JPA entities directly in REST APIs; always use DTOs to prevent lazy loading issues.
- Avoid N+1 query problems by using
@EntityGraphorJOIN FETCHin queries. - Be cautious with
CascadeType.REMOVEon large collections as it can cause performance issues. - Do not use
EAGERfetch type for collections; it can cause excessive database queries. - Avoid long-running transactions as they can cause database lock contention.
- Use
@Transactional(readOnly = true)for read operations to enable optimizations. - Be aware of the first-level cache; entities may not reflect database changes within the same transaction.
- UUID primary keys can cause index fragmentation; consider using sequential UUIDs or Long IDs.
- Pagination on large datasets requires proper indexing to avoid full table scans.