{{announcement.body}}
{{announcement.title}}

Concurrency and Locking With JPA: Everything You Need to Know

DZone 's Guide to

Concurrency and Locking With JPA: Everything You Need to Know

Learn more about concurrency and locking with JPA.

· Database Zone ·
Free Resource

Imagine you have a system used by multiple users where each user is trying to modify the same entity concurrently. How do you ensure that the underlying data's integrity is preserved when accessed concurrently?

The persistence providers an offer locking strategy to manage concurrency. There are two types of locking: Optimistic locking and Pessimistic locking. Before we deep dive into the locking strategy, let's learn a little bit about ACID transactions.

ACID (Atomicity, Consistency, Isolation, Durability) transactions ensure that a database transaction is completed in a timely manner. The relational databases such as MySQL, Postgres, SQL Server, and Oracle are ACID compliant.

A database transaction can be broken down into multiple components. ACID-compliant databases ensure that a transaction is persisted and committed, only when all the components of a transaction are succeeded. If any one of the components fails, the transaction will be rolled back and no change will be made.

In case of multiple transactions, we need to define the locking strategy to make sure that underlying data's integrity is preserved.

Let's look at each locking strategy in this article.

Optimistic Locking

The persistence provider JPA provides javax.persistence.Version annotation to mark a property as a version attribute of an entity. This property can be a numeric (int, long, short) or java.sql.Timestamp field. Only one property with @Version annotation can exist per entity.

Also, the @Version attribute must be in the primary table for an entity mapped to multiple tables.

@Entity
 public class User {

 @Id
 @Column (name = "ID", nullable = false)
 @GeneratedValue (strategy = GenerationType.AUTO)
 private long id;

 @Version
 private long version; @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
 private List<Order> orders = new ArrayList<>(); ..

 }

Here, each transaction that reads data holds the value of the version property. Before any transaction that modifies the underlying data, it checks the value of version property again.

If the value has changed in the meantime an OptimisticLockException will be thrown and transaction will be rolled back. Otherwise, the transaction commits the update and increments the value of version property.

Optimistic Lock Modes

JPA provided two locking modes in case of Optimistic locking: OPTIMISTIC and OPTIMISTIC_FORCE_INCREMENT. OPTIMISTIC obtains the read lock for the entities with @Version property. OPTIMISTIC_FORCE_INCREMENT obtains the read lock for the entities with @Version property and increments the value of the property.

@Service
 public class UserService {

 @PersistenceContext
 private EntityManager entityManager;

 public void updateUser(final String id) {
 User user =
 entityManager.find(User.class, id);
 entityManager.lock(user,
 LockModeType.OPTIMISTIC); ..

 }
 } 
public void updateUser(final String id) {
 User user =
 entityManager.find(User.class, id);
 entityManager.lock(user,
 LockModeType.OPTIMISTIC_FORCE_INCREMENT); ..
 } 

Pessimistic Locking

In case of pessimistic locking, JPA creates a transaction that obtains a lock on the data until the transaction is completed. This prevents other transactions from making any updates to the entity until the lock is released.

Pessimistic locking can be very useful when the data is frequently accessed and modified by multiple transactions.

Keep in mind that using pessimistic locking may result in decreased application performance, if the entities are not susceptible to frequent modifications.

Pessimistic Lock Modes

JPA provides three Pessimistic locking modes: PESSIMISTIC_READ, PESSIMISTIC_FORCE_INCREMENT and PESSIMISTIC_WRITE.

PESSIMISTIC_READ obtains a long-term read lock on the data to prevent the data from being updated or deleted. Other transactions may read the data during the lock, but will not be able to modify or delete the data.

PESSIMISTIC_FORCE_INCREMENT obtains a long-term read lock on the data to prevent the data from being updated or deleted. Also, increments the value of@Version property.

PESSIMISTIC_WRITE obtains a long-term read lock on the data to prevent the data from being read, updated or deleted.

User user =
 entityManager.find(User.class,id);
 entityManager.lock(user,
LockModeType.PESSIMISTIC_WRITE);

 user.getOrders().forEach(order -> {

 orderRepository.delete(order);

 }); .. 

If a pessimistic lock cannot be obtained, it results in a transaction rollback and PessimisticLockException will be thrown. But, if the locking failure doesn't result in a transaction rollback, a LockTimeoutException will be thrown.

Conclusion

Depending on your application needs, choose the right locking strategy and apply locking strategy only if necessary. Feel free to let me know if you have any suggestions or comments below.

Topics:
database ,tutorial ,concurrency and locking ,jpa

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}