All articles
Spring DataExam Tips

Spring Data JPA Exam Guide (2V0-72.22)

April 23, 202619 min read

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:

TermWhat It Is
JPAA Java specification (interfaces and annotations) for ORM. Defines EntityManager, @Entity, JPQL.
HibernateThe most common JPA provider — the actual implementation that talks to the database.
Spring Data JPAA 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.

YOUR CODE
userRepository.findById(1L)
delegates to
Spring Data JPA
generates repository proxy · derives queries from method names
implemented by
JPA
Java Persistence API · specification
EntityManager · @Entity · JPQL
speaks
Hibernate
JPA provider
dirty checking · 1st/2nd-level cache · ORM
executes SQL via
JDBC + Database driver
DATABASE
PostgreSQL · MySQL · Oracle · …

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.


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.

Exam tip: @EnableJpaRepositories is 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 inject EntityManager. The proxy created by @PersistenceContext is what makes EntityManager safe to use as a field in a singleton bean.

Exam tip: EntityManager is 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.

InterfaceExtendsWhat It Adds
Repository<T, ID>Marker interface. Spring detects all sub-interfaces for repository proxy creation. No methods.
CrudRepository<T, ID>RepositoryBasic CRUD: save, findById, findAll, count, delete, existsById.
ListCrudRepository<T, ID>CrudRepositorySame as CrudRepository, but returns List instead of Iterable.
PagingAndSortingRepository<T, ID>RepositoryAdds findAll(Pageable) and findAll(Sort).
JpaRepository<T, ID>ListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutorAdds 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, PagingAndSortingRepository extended CrudRepository and JpaRepository extended PagingAndSortingRepository directly. If your study materials reference the older layout, that is why.

Visualised (Spring Data 3.x):

Spring Data JPA repository interface hierarchyThe Repository marker interface is extended by CrudRepository (basic CRUD methods) and PagingAndSortingRepository (Pageable and Sort support). Each has a List variant (ListCrudRepository, ListPagingAndSortingRepository) that returns List instead of Iterable. JpaRepository extends both List variants and QueryByExampleExecutor, adding JPA-specific methods: flush, saveAndFlush, deleteAllInBatch, getReferenceById.Repository<T, ID>marker · no methodsCrudRepositorysave · findById · findAllcount · delete · existsByIdreturns Iterable<T>PagingAndSortingRepositoryfindAll(Pageable)findAll(Sort)ListCrudRepositoryreturns List<T>ListPagingAndSortingRepositoryList variant of pagingJpaRepository<T, ID>+ flush · saveAndFlush · deleteAllInBatch+ getReferenceById · QueryByExampleExecutor

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: JpaRepository extends ListCrudRepository and ListPagingAndSortingRepository. It is the JPA-specific terminal interface — it adds flush(), saveAndFlush(), deleteAllInBatch(), and getReferenceById() (which returns a lazy proxy without hitting the database).

Exam tip: The @Repository annotation is not required on Spring Data interfaces. Spring detects them via @EnableJpaRepositories (or Boot's auto-config) and creates the proxy automatically. @Repository is 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 page

Spring Data offers three return types for paginated queries:

Return TypeExecutes COUNT QueryUse When
Page<T>YesYou 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>NoYou just want the slice without metadata

Exam tip: Page<T> triggers an extra SELECT COUNT(*) query to compute total count. If you don't need the total, return Slice<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):

VerbPurposeReturn Type
find...By / read...By / get...By / query...ByReadEntity, Optional<Entity>, List<Entity>, Page<Entity>, Stream<Entity>
count...ByCount rowslong
exists...ByCheck existenceboolean
delete...By / remove...ByDeletevoid or long (count of deleted rows)

Operator keywords (predicate):

KeywordTranslates To
Is, Equals= ? (default if omitted)
Not<> ?
LessThan, LessThanEqual< ?, <= ?
GreaterThan, GreaterThanEqual> ?, >= ?
BetweenBETWEEN ? AND ?
Like, NotLikeLIKE ?
StartingWith, EndingWith, ContainingLIKE 'x%', LIKE '%x', LIKE '%x%'
In, NotInIN (?), NOT IN (?)
IsNull, IsNotNullIS NULL, IS NOT NULL
True, False= true, = false
IgnoreCaseLOWER(x) = LOWER(?)
OrderBy<Property>Asc / DescORDER 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 throw PropertyReferenceException and the context will fail to start. Catch these in tests, not in production.

Exam tip: When a property name is ambiguous (e.g., addressCity could mean address.city or a property literally named addressCity), 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: @Modifying queries must run inside a transaction. If the calling code has no @Transactional, you will get TransactionRequiredException. Either annotate the method with @Transactional or rely on a transactional service layer.

Exam tip: Native queries (nativeQuery = true) bypass JPQL parsing. They are not portable across databases — LIMIT works in PostgreSQL/MySQL but not Oracle (which uses ROWNUM or FETCH FIRST).


@Transactional Deep Dive

@Transactional is the most heavily tested topic in the Data Management section. Expect 2-4 questions on propagation, rollback rules, or self-invocation.

How It Works

@Transactional is implemented through Spring AOP. When you put @Transactional on a method, Spring wraps the bean in a proxy. The proxy intercepts the call, asks a PlatformTransactionManager to start (or join) a transaction, invokes the target method, and commits or rolls back based on the outcome.

If you are unsure how proxying works, read the Spring AOP and Pointcut Expressions guide first — every @Transactional gotcha on the exam is really an AOP proxy gotcha.

Where to Put It

@Transactional can go on a class (applies to all public methods) or on individual methods (overrides class-level setting).

@Service
@Transactional(readOnly = true) // class-level default
public class OrderService {
 
    public Order findById(Long id) { /* read-only TX */ }
 
    @Transactional // overrides — full read-write TX
    public Order placeOrder(NewOrderRequest req) { /* ... */ }
}

Two @Transactional Annotations — Watch the Import

There are two annotations called @Transactional and the exam tests whether you can tell them apart:

AnnotationPackageOriginRollback Attribute
Spring'sorg.springframework.transaction.annotation.TransactionalSpring FrameworkrollbackFor / noRollbackFor
Jakarta'sjakarta.transaction.Transactional (formerly javax.transaction.Transactional)Jakarta EE / JTArollbackOn / dontRollbackOn

Spring respects both, but only the Spring one supports propagation, isolation, timeout, and readOnly fully. The Jakarta one has a smaller attribute set and uses different keywords. Mixing them — or assuming the Jakarta one accepts rollbackFor — is a common exam trap.

Exam tip: When code uses @Transactional without explicit attributes, the import line decides the semantics. Always check whether it is org.springframework... or jakarta.transaction... before answering propagation/rollback questions.

Propagation

Propagation determines what happens when a transactional method is called from another transactional context. Spring supports seven values:

PropagationIf a TX existsIf no TX existsTypical Use
REQUIRED (default)Join the existing TXStart a new TXDefault for service methods
REQUIRES_NEWSuspend the outer TX, start a new independent TXStart a new TXAudit logging that must commit even if caller rolls back
SUPPORTSJoin the existing TXRun without a TXHelper methods that work either way
NOT_SUPPORTEDSuspend the existing TX, run without TXRun without TXLong-running non-DB work inside a transactional caller
MANDATORYJoin the existing TXThrow IllegalTransactionStateExceptionEnforce that the caller already started a TX
NEVERThrow IllegalTransactionStateExceptionRun without TXEnforce no surrounding TX
NESTEDCreate a savepoint inside the existing TXStart a new TXPartial rollback within a parent transaction

The three most-tested values behave very differently when an outer transactional method calls an inner transactional method. Visualised:

REQUIRED(default) — inner JOINS outer transaction
Propagation REQUIRED — inner method joins outer transactionouter()inner()TX-1BEGINCOMMITjoin TX-1
  • 1 transaction. Rollback anywhere = full rollback.
  • Inner cannot commit independently of outer.
REQUIRES_NEWinner SUSPENDS outer, opens its own transaction
Propagation REQUIRES_NEW — inner suspends outer and opens its own transactionouter()inner()TX-1TX-1 (resumed)BEGINsuspendresumeCOMMITTX-2 (independent)BEGINCOMMIT
  • 2 independent transactions, separate connections.
  • Inner commits even if outer rolls back (audit logs).
NESTEDinner uses a SAVEPOINT inside outer transaction
Propagation NESTED — inner uses a savepoint inside the outer transactionouter()inner()TX-1 (with savepoint)BEGINsavepointreleaseCOMMITinner work
  • 1 transaction. Inner can roll back to the savepoint without aborting outer.
  • Requires JDBC savepoint support (DataSourceTransactionManager). NOT supported by JpaTransactionManager out of the box.
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void writeAuditLog(AuditEvent event) {
    // Always commits independently of the caller's transaction
}

Exam tip: REQUIRED is the default. If a question doesn't mention propagation explicitly, assume REQUIRED.

Exam tip: REQUIRES_NEW suspends the outer transaction — it doesn't nest it. Both transactions hold their own connections. If both try to write the same row, you get a self-deadlock.

Exam tip: NESTED requires a JDBC driver and DataSource that support savepoints. It is not supported by JPA's JpaTransactionManager out of the box.

Isolation

Isolation controls how concurrent transactions see each other's uncommitted changes.

IsolationDirty ReadNon-Repeatable ReadPhantom Read
READ_UNCOMMITTEDPossiblePossiblePossible
READ_COMMITTEDPreventedPossiblePossible
REPEATABLE_READPreventedPreventedPossible
SERIALIZABLEPreventedPreventedPrevented
DEFAULTUses the database default (PostgreSQL: READ_COMMITTED, MySQL: REPEATABLE_READ)
@Transactional(isolation = Isolation.REPEATABLE_READ)
public Report buildReport() { /* ... */ }

Exam tip: Higher isolation = more locking = lower concurrency. The default (DEFAULT) uses the database's own setting; do not assume it is READ_COMMITTED everywhere.

Rollback Rules

By default, Spring rolls back on RuntimeException and Error. It does NOT roll back on checked exceptions.

This is the single most-missed rule on the exam.

@Transactional
public void chargeCustomer(Order o) throws PaymentException {
    repository.save(o);
    paymentGateway.charge(o); // throws PaymentException (checked)
    // The save above is COMMITTED — checked exceptions do not roll back by default
}

To override:

@Transactional(rollbackFor = PaymentException.class)
public void chargeCustomer(Order o) throws PaymentException { /* ... */ }
 
// Or the inverse — don't roll back even on a runtime exception
@Transactional(noRollbackFor = ValidationException.class)
public void validate(Form f) { /* ... */ }

Exam tip: Default rollback = RuntimeException + Error only. Checked exceptions commit the transaction unless you set rollbackFor.

The Self-Invocation Pitfall

This is the classic exam trap. @Transactional works through a proxy, so a call from inside the same bean to another method on the same bean bypasses the proxy — and therefore bypasses the transaction.

@Service
public class UserService {
 
    public void registerAndNotify(User u) {
        save(u);          // self-invocation — no proxy, no TX
        sendWelcome(u);
    }
 
    @Transactional
    public void save(User u) {
        repository.save(u);
    }
 
    private void sendWelcome(User u) { /* ... */ }
}

When registerAndNotify() calls this.save(), it calls the method directly on the target object. The @Transactional advice never fires. save() runs without a transaction.

Three ways to fix it:

  1. Move the @Transactional method to a different bean — then the call goes through the proxy.
  2. Inject the bean into itself and call through the injected reference (works but smells).
  3. Use AopContext.currentProxy() with @EnableAspectJAutoProxy(exposeProxy = true) — last resort.
// Fix #1 — split into two beans
@Service
public class UserRegistrationService {
    private final UserService userService;
 
    public void registerAndNotify(User u) {
        userService.save(u); // goes through proxy → @Transactional applied
        sendWelcome(u);
    }
}

Exam tip: @Transactional on a private method is silently ignored — proxies cannot intercept private methods.

Exam tip: @Transactional on a final method is also ignored when CGLIB proxies are used — CGLIB proxies subclass the target and cannot override final methods.

Exam tip: @Transactional on protected or package-private methods is also ignored by default. Spring's AnnotationTransactionAttributeSource is configured with publicMethodsOnly = true out of the box — non-public methods are skipped regardless of proxy type.

Exam tip: If you catch the exception inside the @Transactional method and don't rethrow it, no rollback happens. The proxy only sees what bubbles out of the method.

Read-Only Optimization

@Transactional(readOnly = true)
public List<Order> findRecentOrders() { /* ... */ }

readOnly = true is a hint, not enforcement:

  • Hibernate skips dirty checking (no automatic flush at commit) — measurable speedup for read-heavy methods.
  • Some JDBC drivers route the connection to a read replica.
  • The database itself is not told to reject writes.

Exam tip: readOnly = true does not prevent writes. It is a performance hint. A misbehaving method can still execute INSERTs — they just may behave unpredictably.


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: JdbcTemplate is thread-safe once configured and is meant to be used as a singleton bean (which is what Spring Boot gives you).

Exam tip: JdbcTemplate translates JDBC's checked SQLException into Spring's unchecked DataAccessException hierarchy. Your code never has to declare throws 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:

ExceptionThrown When
DataIntegrityViolationExceptionA constraint is violated (NOT NULL, FOREIGN KEY, CHECK)
DuplicateKeyExceptionA unique constraint (typically primary key) is violated
EmptyResultDataAccessExceptionqueryForObject finds zero rows when one was expected
IncorrectResultSizeDataAccessExceptionA query returns more rows than expected (e.g., 2+ for queryForObject)
OptimisticLockingFailureExceptionA @Version-based optimistic lock check fails
CannotAcquireLockExceptionThe database refused a lock (deadlock or lock timeout)
QueryTimeoutExceptionA statement exceeded its configured timeout

All extend DataAccessException, which extends RuntimeException — so they trigger rollback by default.

Exam tip: All DataAccessException subclasses are unchecked (RuntimeException) and trigger @Transactional rollback by default. You don't need rollbackFor to handle data access failures.

Exam tip: JdbcTemplate integrates with @Transactional through DataSourceTransactionManager. JPA uses JpaTransactionManager. 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. A this.foo() call on a transactional method in the same bean bypasses the proxy.
  • @Modifying without @Transactional throws TransactionRequiredException.
  • Catching the exception inside the @Transactional method prevents rollback. The proxy only rolls back if the exception escapes.
  • Checked exceptions do not trigger rollback unless you set rollbackFor. Only RuntimeException and Error do, 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_NEW uses a separate connection. Two transactions touching the same row from the same thread can self-deadlock.
  • @Transactional on private or final methods is silently ignored by the proxy.
  • @Transactional on protected / package-private methods is also silently ignored by default. Spring's AnnotationTransactionAttributeSource has publicMethodsOnly = true out of the box, so non-public methods are skipped regardless of whether you use JDK dynamic or CGLIB proxies.
  • readOnly = true is 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:

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.

Practice This Topic

Reinforce what you've learned with free practice questions and detailed explanations.