This guide provides an end-to-end explanation of Spring Data JPA including project setup, entity creation, repository interfaces, query methods, JPA architecture, persistence lifecycle, and JPQL examples. It also includes diagrams and best practices.
1. Project Setup
Start with Spring Initializr (start.spring.io) and add the following dependencies:
- Spring Web
- Spring Data JPA
- H2 Database (for testing)
Project Structure:
com.example.applicant
├── controller
│ └── ApplicantController.java
├── service
│ └── ApplicantService.java
├── repository
│ └── ApplicantRepository.java
└── model
└── Applicant.java
2. JPA Architecture Overview
Key Components:
- Persistence Unit: A logical grouping of all entity classes.
- Persistence.xml: Configuration for JPA (Spring Boot uses
application.propertiesorapplication.yml). - Persistence Provider (Hibernate): Implements JPA specifications.
- EntityManagerFactory: Creates
EntityManagerinstances. - EntityManager: Performs operations on entities.
- Persistence Context: First-level cache for managed entities.
- Entity: Java POJO mapped to a DB table.
Flow Diagram:
Application ↓ Specification Repository (Optional) ↓ JpaRepository / CrudRepository ↓ EntityManager ↓ Persistence Context ↓ Database
3. Persistence Lifecycle States
States of a JPA Entity:
| State | Description |
|---|---|
| New | Not yet persisted |
| Managed | Tracked by EntityManager |
| Detached | No longer tracked |
| Removed | Marked for deletion |
State Transitions:
- New → Managed → Detached → Removed
- Use
persist(),merge(),detach(),remove()
4. Entity Definition and Annotations
@Entity
public class Applicant {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String status;
}
Common Annotations:
@Entity: Declares JPA entity.@Id: Primary key.@GeneratedValue: Auto ID generation.@Column,@Table: Customize mapping.
5. Repository Interfaces
CrudRepository
public interface ApplicantRepository extends CrudRepository<Applicant, Long> {}
PagingAndSortingRepository
Pageable pageable = PageRequest.of(0, 5); Page<Applicant> applicants = repository.findAll(pageable);
JpaRepository
public interface ApplicantRepository extends JpaRepository<Applicant, Long> {}
Specification Repository (Dynamic Filtering)
Used for dynamic and complex queries using criteria.
public interface ApplicantRepository extends JpaRepository<Applicant, Long>, JpaSpecificationExecutor<Applicant> {}
Specification<Applicant> hasStatus(String status) = (root, query, builder) ->
builder.equal(root.get("status"), status);
Repository Hierarchy:
JpaRepository ├── PagingAndSortingRepository │ └── CrudRepository └── JpaSpecificationExecutor (optional)
6. Query Methods and Custom Queries
Interface-Based Query Method Examples:
Spring Data JPA automatically implements query logic based on method naming conventions in repository interfaces.
You can combine multiple keywords like IgnoreCase, Containing, StartingWith, etc., to build expressive and powerful query methods.
Spring Data JPA automatically implements query logic based on method naming conventions in repository interfaces.
List<Applicant> findByStatus(String status); List<Applicant> findByNameAndStatus(String name, String status); List<Applicant> findByStatusOrderByNameAsc(String status); / Finds applicants with exact name List<Applicant> findByName(String name); // Finds applicants with name exactly (case-insensitive) List<Applicant> findByNameIgnoreCase(String name); // Finds applicants whose name contains the given string (case-sensitive) List<Applicant> findByNameContaining(String keyword); // Finds applicants whose name contains the string, case-insensitive List<Applicant> findByNameContainingIgnoreCase(String keyword); // Finds applicants with name starting with a prefix (case-insensitive) List<Applicant> findByNameStartingWithIgnoreCase(String prefix); // Combines name containing + status filter + ignore case List<Applicant> findByNameContainingIgnoreCaseAndStatusIgnoreCase(String name, String status); // Finds applicants by name ignoring case List<Applicant> findByNameIgnoreCase(String name); // Finds applicants whose name contains the given string List<Applicant> findByNameContaining(String keyword); // Finds applicants with name starting with a prefix List<Applicant> findByNameStartingWith(String prefix); // Finds applicants with status and name List<Applicant> findByNameAndStatus(String name, String status); // Finds applicants with either of two statuses List<Applicant> findByStatusOrStatus(String status1, String status2); // Finds applicants with ID between two values List<Applicant> findByIdBetween(Long start, Long end); // Finds applicants with status in a list List<Applicant> findByStatusIn(List<String> statuses); // Finds all applicants ordered by name ascending List<Applicant> findAllByOrderByNameAsc();
JPQL Using @Query:
@Query("SELECT a FROM Applicant a WHERE a.name LIKE %:name%")
List<Applicant> searchByPartialName(@Param("name") String name);
@Query("SELECT a FROM Applicant a WHERE a.status = :status")
List<Applicant> findByStatusJPQL(@Param("status") String status);
// WHERE and AND
@Query("SELECT a FROM Applicant a WHERE a.status = 'active' AND a.name = 'John'")
List<Applicant> activeNamedJohn();
// OR
@Query("SELECT a FROM Applicant a WHERE a.status = 'active' OR a.status = 'pending'")
List<Applicant> activeOrPending();
// BETWEEN
@Query("SELECT a FROM Applicant a WHERE a.id BETWEEN :start AND :end")
List<Applicant> findInRange(@Param("start") Long start, @Param("end") Long end);
// IN
@Query("SELECT a FROM Applicant a WHERE a.status IN :statuses")
List<Applicant> findByStatuses(@Param("statuses") List<String> statuses);
// ORDER BY
@Query("SELECT a FROM Applicant a ORDER BY a.name ASC")
List<Applicant> findAllSortedByName();
// LIKE with wildcard
@Query("SELECT a FROM Applicant a WHERE a.name LIKE :pattern")
List<Applicant> findByPattern(@Param("pattern") String pattern);
7. Repository Comparison
Spring Data JPA offers multiple repository interfaces with increasing levels of functionality. Here’s a detailed comparison:
| Repository Interface | Extends | Key Features | Return Type |
CrudRepository | – | Basic CRUD methods like save, findById, deleteById | Optional, Iterable |
PagingAndSortingRepository | CrudRepository | Adds pagination and sorting methods (findAll(Pageable), findAll(Sort)) | Page, Sort |
JpaRepository | PagingAndSortingRepository | Adds batch operations (flush, saveAllAndFlush) and JPA-specific features | List, Optional, Page |
ListCrudRepository | – (Spring Data 3+) | Same as CrudRepository but returns List instead of Iterable | List |
ListPagingAndSortingRepository | ListCrudRepository | Like PagingAndSortingRepository, but returns List instead of Iterable or Page | List |
JpaSpecificationExecutor | – (used alongside JpaRepository) | Allows building dynamic queries using the Criteria API | List, Page |
Common JPA Annotations:
@Entity: Marks a class as a persistent entity.@Table(name = "applicants"): Specifies the table name for the entity.@Id: Specifies the primary key field.@GeneratedValue(strategy = ...): Defines how the primary key is generated.@Column(name = "applicant_name"): Maps a field to a specific column.@Transient: Marks a field not to be persisted.@Embedded: Marks a field as an embeddable type.@Embeddable: Marks a class whose fields can be embedded in an entity.@Enumerated(EnumType.STRING): Used for enum mapping to string or ordinal.@Lob: Used for large objects like CLOB/BLOB.@Temporal(TemporalType.DATE): Used withDateorCalendarto specify the precision.
Spring Data & Query Annotations:
@Query: Allows defining custom JPQL or native SQL inside repository interfaces.@Param: Used to bind method parameters to query parameters.@Modifying: Indicates that the query method modifies data (e.g.,update,delete).@Transactional: Manages transaction boundaries in methods. Needed with@Modifying.@EnableJpaRepositories: Enables JPA repositories and scanning.@Repository: Marks an interface or class as a Spring-managed data component.