Spring Data JPA: Entities and Repositories
Introduction
Spring Data JPA maps Java objects to database tables and generates query methods from method names—less SQL boilerplate than JDBC for CRUD-heavy apps. This chapter adds JPA to a Spring Boot 3 project, defines entities and repositories, and compares when to choose JPA versus MyBatis.
Prerequisites
- JDBC DataSource and Connection Pool Basics
- MyBatis Integration (optional comparison)
- MySQL or PostgreSQL running locally
- Basic SQL and Java classes
Add Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>PostgreSQL: use org.postgresql:postgresql instead of MySQL driver.
Configure JPA
application.yml:
ddl-auto | Behavior |
|---|---|
| none | No schema changes—production default |
| validate | Check schema matches entities |
| update | Dev-friendly—adds columns/tables |
| create-drop | Recreate on each restart—tests only |
Warning
Do Not Use update/create-drop in Production
Use migrations (Flyway/Liquibase) for controlled schema changes.
Define an Entity
Code explanation:
@Entitymarks a persistent class@Tablemaps to DB table nameGenerationType.IDENTITYmatches MySQLAUTO_INCREMENT
Repository Interface
package com.example.demo.user;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}Code explanation:
JpaRepositoryprovidessave,findById,delete, pagination- Method name
findByEmailgeneratesWHERE email = ?query
Service Layer
REST Controller
Test:
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"email":"ada@example.com","displayName":"Ada"}'Pagination and Sorting
Repository:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Page<User> findByDisplayNameContainingIgnoreCase(String keyword, Pageable pageable);Service call:
Page<User> page = userRepository.findByDisplayNameContainingIgnoreCase(
"a",
PageRequest.of(0, 10, Sort.by("createdAt").descending())
);JPA vs MyBatis
| Choose JPA | Choose MyBatis |
|---|---|
| CRUD-heavy domains | Complex SQL, reports |
| Rapid prototyping | DBA-tuned SQL control |
| Object graphs with relationships | Legacy SQL migration |
Many teams use JPA for simple domains and MyBatis for heavy queries—both can coexist with clear boundaries.
See MySQL track for SQL fundamentals.
FAQ
LazyInitializationException?
Access lazy associations inside @Transactional service method or use DTO projection.
Entity returned from REST exposes too much?
Map to DTO or use @JsonIgnore on sensitive fields.
Table not created?
Check ddl-auto, DB credentials, and entity @Entity scan in main package.
Hibernate SQL dialect wrong?
Usually auto-detected; set spring.jpa.database-platform only when needed.
Replace MyBatis entirely?
Not required—pick per module or phase.
Connection pool same as JDBC chapter?
Yes—JPA uses same DataSource and HikariCP.