What Is Spring Data JPA
Spring Data JPA is an abstraction layer that sits on top of the Java Persistence API (JPA) and removes most of the boilerplate involved in writing data access code. Instead of implementing DAOs by hand, you declare a repository interface and Spring Data generates the implementation at runtime.
To keep three terms straight:
| Term | What It Is |
|---|---|
| JPA | A Java specification (interfaces and annotations) for ORM. Defines EntityManager, @Entity, JPQL. |
| Hibernate | The most common JPA provider — the actual implementation that talks to the database. |
| Spring Data JPA | A Spring project that auto-implements repository interfaces on top of any JPA provider (usually Hibernate). |
Spring Data JPA does not replace Hibernate — it sits above it. When you call userRepository.findById(1L), Spring Data delegates to the JPA EntityManager, which delegates to Hibernate, which executes SQL.
Each layer delegates downward and depends only on the layer immediately below — you can swap Hibernate for EclipseLink without touching Spring Data, and you can drop Spring Data entirely and call JPA directly when needed.
On the 2V0-72.22 exam, Spring Data JPA falls under the Data Management section, which carries roughly 10-12% of all questions. Expect questions on the repository hierarchy, @Transactional propagation, rollback rules, derived query method naming, and the difference between JdbcTemplate and JPA-based access.
This guide walks through everything in that section, with code examples and the exam traps that cost candidates points. For the entity-mapping deep dive — relationships, fetch strategies, cascading, inheritance, and N+1 — see the companion JPA Entity Mapping Exam Guide.
Setup & Auto-configuration
To use Spring Data JPA in a Spring Boot application, add the starter:
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>Gradle:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'Once the starter is on the classpath, Spring Boot auto-configures everything you need: a DataSource, an EntityManagerFactory, a PlatformTransactionManager, and @EnableJpaRepositories (so it scans for repository interfaces).
In a plain Spring application (no Boot), you have to enable repository scanning yourself:
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repo")
public class JpaConfig { /* ... */ }If you want to dig into how JpaRepositoriesAutoConfiguration and DataSourceAutoConfiguration decide what to register, see Spring Boot Auto-configuration Explained.
For repository slice testing, @DataJpaTest, and transactional rollback behavior in tests, see the Spring Boot Testing Exam Guide.
Exam tip:
@EnableJpaRepositoriesis applied automatically by Spring Boot. You only add it manually in non-Boot Spring applications, or to override the default scan path.
EntityManager and @PersistenceContext
You rarely touch EntityManager directly when using Spring Data, but the exam expects you to know how it gets injected when you do — for example in a custom repository implementation.
@Repository
public class OrderQueryDao {
@PersistenceContext
private EntityManager em;
public List<Order> findRecent(int limit) {
return em.createQuery("SELECT o FROM Order o ORDER BY o.createdAt DESC", Order.class)
.setMaxResults(limit)
.getResultList();
}
}@PersistenceContext does not inject the raw EntityManager — it injects a thread-safe shared proxy that delegates to the real EntityManager bound to the current transaction. That's why a singleton DAO can safely hold an EntityManager field.
Exam tip: Use
@PersistenceContext, not@Autowired, to injectEntityManager. The proxy created by@PersistenceContextis what makesEntityManagersafe to use as a field in a singleton bean.
Exam tip:
EntityManageris not thread-safe by itself. The Spring/JPA injection magic gives you a transaction-scoped proxy, not the underlying instance.
Repository Hierarchy
Every Spring Data repository extends from a small family of marker interfaces. The exam expects you to know what each level adds.
| Interface | Extends | What It Adds |
|---|---|---|
Repository<T, ID> | — | Marker interface. Spring detects all sub-interfaces for repository proxy creation. No methods. |
CrudRepository<T, ID> | Repository | Basic CRUD: save, findById, findAll, count, delete, existsById. |
ListCrudRepository<T, ID> | CrudRepository | Same as CrudRepository, but returns List instead of Iterable. |
PagingAndSortingRepository<T, ID> | Repository | Adds findAll(Pageable) and findAll(Sort). |
JpaRepository<T, ID> | ListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutor | Adds JPA-specific operations: flush, saveAndFlush, deleteAllInBatch, getReferenceById. |
Version note: This hierarchy reflects Spring Data 3.x (released late 2022, used with Spring Framework 6 / Spring Boot 3). In Spring Data 2.x,
PagingAndSortingRepositoryextendedCrudRepositoryandJpaRepositoryextendedPagingAndSortingRepositorydirectly. If your study materials reference the older layout, that is why.
Visualised (Spring Data 3.x):
Spring Data creates a proxy implementation of your interface at startup. The proxy is itself a Spring bean — created during the bean lifecycle like any other component, with all the BeanPostProcessor hooks applied.
A typical repository declaration:
public interface UserRepository extends JpaRepository<User, Long> {
// No code needed — Spring Data implements all CRUD and JPA methods automatically
}You can also extend just CrudRepository if you want a leaner API and don't need JPA-specific methods:
public interface ProductRepository extends CrudRepository<Product, Long> {
Optional<Product> findBySku(String sku);
}Exam tip:
JpaRepositoryextendsListCrudRepositoryandListPagingAndSortingRepository. It is the JPA-specific terminal interface — it addsflush(),saveAndFlush(),deleteAllInBatch(), andgetReferenceById()(which returns a lazy proxy without hitting the database).
Exam tip: The
@Repositoryannotation is not required on Spring Data interfaces. Spring detects them via@EnableJpaRepositories(or Boot's auto-config) and creates the proxy automatically.@Repositoryis for traditional@Component-style DAOs.
Paging and Sorting
PagingAndSortingRepository (and therefore JpaRepository) gives you Pageable and Sort for free:
public interface OrderRepository extends JpaRepository<Order, Long> {
Page<Order> findByStatus(OrderStatus status, Pageable pageable);
List<Order> findByCustomerId(Long customerId, Sort sort);
}
// Usage
Pageable page = PageRequest.of(0, 20, Sort.by("createdAt").descending());
Page<Order> results = orderRepository.findByStatus(OrderStatus.OPEN, page);
results.getTotalElements(); // total row count (executes COUNT query)
results.getTotalPages(); // total page count
results.getContent(); // List<Order> for current pageSpring Data offers three return types for paginated queries:
| Return Type | Executes COUNT Query | Use When |
|---|---|---|
Page<T> | Yes | You need total count / total pages (UI pagination with "page X of Y") |
Slice<T> | No | "Load more" UX — you only need to know if a next page exists |
List<T> | No | You just want the slice without metadata |
Exam tip:
Page<T>triggers an extraSELECT COUNT(*)query to compute total count. If you don't need the total, returnSlice<T>to avoid the cost.
Derived Query Methods
The most distinctive feature of Spring Data is query derivation from method names. You declare a method following a naming convention and Spring Data generates the JPQL query at startup.
The pattern is:
<verb> [Distinct] [Top<N> | First<N>] By <Property> [Operator] [And|Or <Property> [Operator]] [OrderBy <Property> Asc|Desc]
Verbs (subject keywords):
| Verb | Purpose | Return Type |
|---|---|---|
find...By / read...By / get...By / query...By | Read | Entity, Optional<Entity>, List<Entity>, Page<Entity>, Stream<Entity> |
count...By | Count rows | long |
exists...By | Check existence | boolean |
delete...By / remove...By | Delete | void or long (count of deleted rows) |
Operator keywords (predicate):
| Keyword | Translates To |
|---|---|
Is, Equals | = ? (default if omitted) |
Not | <> ? |
LessThan, LessThanEqual | < ?, <= ? |
GreaterThan, GreaterThanEqual | > ?, >= ? |
Between | BETWEEN ? AND ? |
Like, NotLike | LIKE ? |
StartingWith, EndingWith, Containing | LIKE 'x%', LIKE '%x', LIKE '%x%' |
In, NotIn | IN (?), NOT IN (?) |
IsNull, IsNotNull | IS NULL, IS NOT NULL |
True, False | = true, = false |
IgnoreCase | LOWER(x) = LOWER(?) |
OrderBy<Property>Asc / Desc | ORDER BY x ASC / DESC |
Examples building from simple to complex:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByLastNameAndFirstName(String last, String first);
List<User> findByAgeBetween(int min, int max);
List<User> findByEmailContainingIgnoreCase(String fragment);
List<User> findByStatusInOrderByCreatedAtDesc(Collection<Status> statuses);
long countByStatus(Status status);
boolean existsByEmail(String email);
// Property path traversal — User has an Address, Address has a city
List<User> findByAddress_City(String city);
// Limiting results
Optional<User> findFirstByOrderByCreatedAtDesc();
List<User> findTop5ByStatusOrderByCreatedAtDesc(Status status);
}Exam tip: Derived query method names are validated at application startup. A typo in a property name (
findByEmial) will throwPropertyReferenceExceptionand the context will fail to start. Catch these in tests, not in production.
Exam tip: When a property name is ambiguous (e.g.,
addressCitycould meanaddress.cityor a property literally namedaddressCity), use an underscore as an explicit separator:findByAddress_City.
@Query — JPQL and Native
Derived methods cover most cases, but they get unwieldy with more than two or three predicates. For anything more complex, use @Query.
JPQL with named parameters:
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o WHERE o.customer.email = :email AND o.status = :status")
List<Order> findByCustomerEmailAndStatus(@Param("email") String email,
@Param("status") OrderStatus status);
}JPQL with positional parameters:
@Query("SELECT o FROM Order o WHERE o.total > ?1")
List<Order> findOrdersOverAmount(BigDecimal amount);Native SQL:
@Query(value = "SELECT * FROM orders WHERE total > :amount",
nativeQuery = true)
List<Order> findOrdersOverAmountNative(@Param("amount") BigDecimal amount);Modifying queries (UPDATE / DELETE):
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status WHERE u.lastLoginAt < :cutoff")
int deactivateInactiveUsers(@Param("status") Status status,
@Param("cutoff") Instant cutoff);Exam tip:
@Modifyingqueries must run inside a transaction. If the calling code has no@Transactional, you will getTransactionRequiredException. Either annotate the method with@Transactionalor rely on a transactional service layer.
Exam tip: Native queries (
nativeQuery = true) bypass JPQL parsing. They are not portable across databases —LIMITworks in PostgreSQL/MySQL but not Oracle (which usesROWNUMorFETCH FIRST).
@Transactional
@Transactional is the densest topic in Data Management — propagation, isolation, rollback rules, and the self-invocation pitfall together account for 2-4 exam questions. The full breakdown lives in a dedicated guide: Spring @Transactional Exam Guide. Read it before the exam.
Exam tip:
REQUIREDis the default. Default rollback coversRuntimeExceptionandErroronly — checked exceptions commit.@Transactionalon private, non-public, or final methods is silently ignored under CGLIB proxies.
JdbcTemplate (Briefly)
The Data Management section also covers Spring's lower-level JDBC abstraction, JdbcTemplate. It is not deprecated and remains the right tool when you don't want JPA's overhead — for simple read paths, complex SQL, or performance-critical code.
@Repository
public class CustomerJdbcDao {
private final JdbcTemplate jdbc;
public CustomerJdbcDao(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
public Customer findById(Long id) {
return jdbc.queryForObject(
"SELECT id, name, email FROM customers WHERE id = ?",
(rs, rowNum) -> new Customer(rs.getLong("id"),
rs.getString("name"),
rs.getString("email")),
id
);
}
public int updateEmail(Long id, String email) {
return jdbc.update(
"UPDATE customers SET email = ? WHERE id = ?",
email, id
);
}
}JdbcTemplate is auto-configured by Spring Boot whenever a DataSource bean exists.
Exam tip:
JdbcTemplateis thread-safe once configured and is meant to be used as a singleton bean (which is what Spring Boot gives you).
Exam tip:
JdbcTemplatetranslates JDBC's checkedSQLExceptioninto Spring's uncheckedDataAccessExceptionhierarchy. Your code never has to declarethrows SQLException.
The same translation applies to JPA exceptions — Hibernate's ConstraintViolationException, OptimisticLockException, etc. all get re-thrown as DataAccessException subclasses. The most common ones to recognize on the exam:
| Exception | Thrown When |
|---|---|
DataIntegrityViolationException | A constraint is violated (NOT NULL, FOREIGN KEY, CHECK) |
DuplicateKeyException | A unique constraint (typically primary key) is violated |
EmptyResultDataAccessException | queryForObject finds zero rows when one was expected |
IncorrectResultSizeDataAccessException | A query returns more rows than expected (e.g., 2+ for queryForObject) |
OptimisticLockingFailureException | A @Version-based optimistic lock check fails |
CannotAcquireLockException | The database refused a lock (deadlock or lock timeout) |
QueryTimeoutException | A statement exceeded its configured timeout |
All extend DataAccessException, which extends RuntimeException — so they trigger rollback by default.
Exam tip: All
DataAccessExceptionsubclasses are unchecked (RuntimeException) and trigger@Transactionalrollback by default. You don't needrollbackForto handle data access failures.
Exam tip:
JdbcTemplateintegrates with@TransactionalthroughDataSourceTransactionManager. JPA usesJpaTransactionManager. Spring Boot picks the right one based on what's on the classpath.
Common Exam Traps
A condensed checklist of the gotchas that cost points. Skim this before the exam.
- Self-invocation skips
@Transactional. Athis.foo()call on a transactional method in the same bean bypasses the proxy. @Modifyingwithout@TransactionalthrowsTransactionRequiredException.- Catching the exception inside the
@Transactionalmethod prevents rollback. The proxy only rolls back if the exception escapes. - Checked exceptions do not trigger rollback unless you set
rollbackFor. OnlyRuntimeExceptionandErrordo, by default. - Lazy loading outside a transaction throws
LazyInitializationException. Initialize collections inside the transactional method or use a fetch join. - Derived query property typos crash the application context at startup with
PropertyReferenceException. REQUIRES_NEWuses a separate connection. Two transactions touching the same row from the same thread can self-deadlock.@Transactionalonprivateorfinalmethods is silently ignored by the proxy.@Transactionalonprotected/ package-private methods is also silently ignored by default. Spring'sAnnotationTransactionAttributeSourcehaspublicMethodsOnly = trueout of the box, so non-public methods are skipped regardless of whether you use JDK dynamic or CGLIB proxies.readOnly = trueis a hint, not enforcement. A method can still write — the database will not stop it.
Frequently Asked Questions
Is Spring Data JPA on the 2V0-72.22 exam?
Yes. The Data Management section covers Spring Data JPA repositories, @Transactional (propagation, isolation, rollback rules), and JdbcTemplate. It accounts for roughly 10-12% of the exam. Plan to spend solid time on @Transactional — it is the densest topic.
What's the difference between JPA, Hibernate, and Spring Data JPA?
JPA is the specification (interfaces and annotations). Hibernate is the most common JPA provider — it implements the spec. Spring Data JPA is a Spring project that auto-implements your repository interfaces on top of any JPA provider, eliminating most DAO boilerplate.
Which propagation level is the default?
Propagation.REQUIRED. If a transaction is already in progress, the method joins it. If not, a new transaction starts.
When does @Transactional roll back?
By default, only on RuntimeException and Error. Checked exceptions commit the transaction unless you explicitly set rollbackFor = SomeCheckedException.class.
Do I need to know JdbcTemplate for the exam?
Yes — at a basic level. Know that it is thread-safe, that it translates SQLException into Spring's DataAccessException hierarchy, that it is auto-configured when a DataSource exists, and the difference between query, queryForObject, and update. Deep SQL knowledge is not required.
Next Steps
This guide covers the Data Management section of the 2V0-72.22 exam end to end. To round out the rest of the syllabus:
- JPA Entity Mapping Exam Guide — sister deep dive on relationships, fetch strategies, cascading, inheritance, and N+1.
- Spring AOP and Pointcut Expressions — the proxy mechanism behind every
@Transactionalannotation. - Spring Bean Lifecycle Explained — how repositories and other proxies are built.
- Spring Boot Auto-configuration Explained — how
JpaRepositoriesAutoConfigurationandDataSourceAutoConfigurationwire your data layer for free. - Spring Professional Exam Questions 2025 — sample questions across the full syllabus.
When you are ready to practice with realistic exam-style questions on Spring Data JPA and @Transactional, our question bank has hundreds of items modeled on the 2V0-72.22 format — including the propagation and rollback edge cases that catch most candidates.