Partner – JPA Buddy – NPI (tag=JPA/Hibernate)
announcement - icon

The right tools can and will save a lot of time. As long as you are using Hibernate and IntelliJ IDEA you can boost your coding speed and quality with JPA Buddy. It will help in a lot of the day-to-day work:

  • Creating JPA entities that follow best practices for efficient mapping
  • Creating DTOs from entities and MapStruct mappers using convenient visual tools
  • Generating entities from the existing database or Swagger-generated POJOs
  • Visually composing methods for Spring Data JPA repositories
  • Generating differential SQL to update your schema in accordance with your changes in entities
  • Autogenerating Flyway migrations and Liquibase changelogs comparing entities with the database or two databases
  • … and a lot more

Simply put, you'll learn and use the best practices of Hibernate and surrounding technology and become a lot more!

Definitely visit the JPA Buddy site to see its features in action closer.

Persistence top

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE

1. Overview

In this quick tutorial, we'll discuss enabling transaction locks in Spring Data JPA for custom query methods and predefined repository CRUD methods.

We'll also learn about different lock types and setting transaction lock timeouts.

2. Lock Types

JPA has two main lock types defined, Pessimistic Locking and Optimistic Locking.

2.1. Pessimistic Locking

When we use Pessimistic Locking in a transaction, and access an entity, it'll be locked immediately. The transaction releases the lock either by committing or rolling back the transaction.

2.2. Optimistic Locking

In Optimistic Locking, the transaction doesn't lock the entity immediately. Instead, the transaction commonly saves the entity's state with a version number assigned to it.

When we try to update the entity's state in a different transaction, the transaction compares the saved version number with the existing version number during the update.

At this point, if the version number differs, it means that we can't modify the entity. If there's an active transaction, then that transaction will be rolled back and the underlying JPA implementation will throw an OptimisticLockException.

Depending on what's most suitable for our current development context, we can also use other approaches, such as timestamps, hash value computation, or serialized checksum.

3. Enabling Transaction Locks on Query Methods

To acquire a lock on an entity, we can annotate the target query method with a Lock annotation by passing the required lock mode type.

Lock mode types are enum values to be specified while locking an entity. The specified lock mode is then propagated to the database to apply the corresponding lock on the entity object.

To specify a lock on a custom query method of a Spring Data JPA repository, we can annotate the method with @Lock and specify the required lock mode type:

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("SELECT c FROM Customer c WHERE c.orgId = ?1")
public List<Customer> fetchCustomersByOrgId(Long orgId);

To enforce the lock on predefined repository methods, such as findAll or findById(id), we have to declare the method within the repository and annotate the method with the Lock annotation:

@Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Customer> findById(Long customerId);

When the lock is explicitly enabled, and there's no active transaction, the underlying JPA implementation will throw a TransactionRequiredException.

If the lock can't be granted, and the locking conflict doesn't result in a transaction rollback, JPA throws a LockTimeoutException, but it doesn't mark the active transaction for rollback.

4. Setting Transaction Lock Timeouts

When using Pessimistic Locking, the database will try to lock the entity immediately. The underlying JPA implementation throws a LockTimeoutException when the lock can't be obtained immediately. To avoid such exceptions, we can specify the lock timeout value.

In Spring Data JPA, the lock timeout can be specified using the QueryHints annotation by placing a QueryHint on query methods:

@Lock(LockModeType.PESSIMISTIC_READ)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
public Optional<Customer> findById(Long customerId);

We can find further details on setting the lock timeout hint at different scopes in this ObjectDB article.

5. Conclusion

In this article, we explored the different types of transaction lock modes. Then we learned how to enable transaction locks in Spring Data JPA. We also covered setting lock timeouts.

Applying the right transaction locks at the right places can help to maintain data integrity in high-volume concurrent usage applications.

When the transaction needs to strictly adhere to ACID rules, we should use Pessimistic Locking. Optimistic Locking should be applied when we need to allow multiple concurrent reads and when eventual consistency is acceptable within the application context.

As always, the sample code for both Pessimistic Locking and Optimistic Locking can be found over on Github.

Persistence bottom

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE
Persistence footer banner
Comments are closed on this article!