💾 Spring Data JPA
Simplify database access with JPA entities, repositories, and Hibernate ORM.
1. ORM Concept
Relational databases represent data in tables, while Java represents data as objects. This difference causes the Object-Relational Mismatch problem. ORM (Object-Relational Mapping) frameworks bridge this gap by automatically converting data between Java objects and database tables.
| Technology | Description |
|---|---|
| JPA (Java Persistence API) | A Java specification (interfaces and annotations) outlining how to manage relational data. It is just an API, not an implementation (package: jakarta.persistence.*). |
| Hibernate | The most popular implementation (provider) of the JPA specification. It does the heavy lifting of translating JPA concepts to actual SQL queries. |
| Spring Data JPA | A Spring abstraction layer built on top of JPA/Hibernate. It drastically reduces boilerplate code by automatically providing repository implementations and query generation. |
2. JPA Entities
An entity is a lightweight Java class whose state is persisted to a table in a relational database. We map fields to columns using annotations.
@Entity: Marks the class as a JPA entity.@Table(name="..."): Specifies the DB table name.@Id: Defines the primary key.@GeneratedValue: Specifies the primary key generation strategy (IDENTITY, SEQUENCE, AUTO).@Column(name="...", nullable=false, unique=true): Customizes the column mapping.@Transient: Ignores the field; it will not be persisted.
3. Entity Relationships
Databases use foreign keys to relate tables. JPA maps these relationships to object references.
@OneToOne: 1-to-1 relationship.@OneToMany/@ManyToOne: 1-to-N relationship (e.g., Department has many Employees). The "Many" side usually holds the foreign key (@JoinColumn).@ManyToMany: M-to-N relationship (e.g., Students and Courses). Requires a junction table (@JoinTable).
Fetch Types: EAGER fetches related entities immediately (can cause N+1 query performance issues). LAZY fetches them only when accessed.
4. Spring Data JPA Repositories
Instead of writing DAO (Data Access Object) classes with repetitive JDBC or Hibernate code, you just define an interface. Spring generates the implementation at runtime.
The hierarchy: Repository → CrudRepository → PagingAndSortingRepository → JpaRepository.
JpaRepository gives you full CRUD, pagination, and sorting capabilities out of the box.
5. Derived Query Methods
Spring Data JPA can automatically generate queries simply by looking at the method name! It parses terms like findBy, LessThan, And, and maps them to SQL.
6. JPQL (Java Persistence Query Language)
For complex queries that make method names too long, use the @Query annotation. JPQL operates on Entity classes and fields, not database tables and columns. You can also use native SQL.
7. Pagination and Sorting
To avoid loading thousands of rows into memory, use Pagination. Pass a Pageable object to your repository method.
8. Configuration
Configure JPA in application.properties.
9. Complete Mini Project: E-Commerce
A quick look at multiple entities working together.
10. Auditing with Spring Data
Auditing automatically tracks who created/modified an entity and when.
- Add
@EnableJpaAuditingto your main app class. - Add
@EntityListeners(AuditingEntityListener.class)to your entity.
11. Caching
Spring provides an easy caching abstraction. Enable it with @EnableCaching.
12. Best Practices + Common Pitfalls
| Problem / Pitfall | Solution / Best Practice |
|---|---|
| N+1 Query Problem (Fetching a list of parents causes N additional queries for children). | Use @Query("SELECT p FROM Parent p JOIN FETCH p.children") or EntityGraphs to load related data in a single query. |
| Exposing Entities in APIs (Infinite recursion, leaking DB structure). | Always map Entities to DTOs (Data Transfer Objects) before returning them in a @RestController. Use MapStruct or ModelMapper. |
| LazyInitializationException (Trying to access a lazy collection outside an open session). | Ensure the database transaction spans the service layer using @Transactional. Initialize collections inside the transaction. |
Using save() for updates (Can trigger unnecessary SELECTs). |
Within a @Transactional method, simply fetching an entity and modifying its setters will automatically issue an UPDATE when the transaction commits (Dirty Checking). |