Spring Boot HandBook

    Understanding Optimistic and Pessimistic Locking in JPA#

    Introduction:#

    Concurrency control is essential in database systems where multiple transactions occur simultaneously. In JPA, locking mechanisms such as Optimistic Locking and Pessimistic Locking help manage concurrent access to entities. This article explores these locking strategies, explains when to use each, and provides practical examples.

    1. What is Database Locking?#

    Locking mechanisms in databases are used to prevent dirty reads, non-repeatable reads, and phantom reads by managing access to data when multiple transactions occur. Locking ensures data consistency and integrity, especially in concurrent environments.

    2. Types of Locking in JPA#

    Optimistic Locking:#

    • Definition: Optimistic locking assumes that multiple transactions can complete without affecting each other, and it only checks for conflicts at the time of committing.
    • Usage: Best suited for scenarios where data conflicts are rare.
    • How It Works: Entities are checked for modifications using a version field (usually a column like @Version). If the version doesn't match during a commit, it indicates a conflict, and an exception is thrown.
    • Annotations: @Version

    Pessimistic Locking:#

    • Definition: Pessimistic locking assumes that conflicts will occur, and it locks the data from the moment a transaction starts until it completes.
    • Usage: Best for cases where there's a high likelihood of conflicts.
    • How It Works: A row is locked in the database until the transaction completes, ensuring that no other transactions can modify the data.
    • Annotations: LockModeType.PESSIMISTIC_READ, LockModeType.PESSIMISTIC_WRITE

    3. Optimistic Locking in JPA (with Example)#

    Optimistic locking uses the @Version annotation to keep track of the entity's version and detect concurrent modifications.

    Example:#

    @Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int quantity; @Version private int version; // Version field for optimistic locking // Getters and Setters }

    In this example, the Product entity uses a version field to implement optimistic locking. When an update is made, JPA checks if the version matches the one stored in the database. If not, an OptimisticLockException is thrown.

    Service Layer Example:#

    @Transactional public void updateProductQuantity(Long productId, int newQuantity) { Product product = productRepository.findById(productId).orElseThrow(); product.setQuantity(newQuantity); productRepository.save(product); // Version is checked here }

    If two transactions try to update the same product concurrently, the one that finishes first will succeed, while the other will fail due to a version mismatch.

    4. Pessimistic Locking in JPA (with Example)#

    Pessimistic locking ensures that data is locked during the entire transaction, preventing other transactions from reading or modifying it.

    Example:#

    @Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int quantity; // No version field needed for pessimistic locking }

    Service Layer Example with Pessimistic Locking:#

    @Transactional public void updateProductQuantityWithLock(Long productId, int newQuantity) { Product product = productRepository.findById(productId, LockModeType.PESSIMISTIC_WRITE) .orElseThrow(); product.setQuantity(newQuantity); productRepository.save(product); }

    In this case, the findById method uses LockModeType.PESSIMISTIC_WRITE, which locks the row in the database until the transaction completes.

    5. When to Use Optimistic vs. Pessimistic Locking#

    Optimistic Locking:#

    • Use when the likelihood of concurrent data conflicts is low.
    • Best for systems with high read-to-write ratios.
    • Example: A ticket booking system where many users are querying availability but only a few are confirming purchases at the same time.

    Pessimistic Locking:#

    • Use when the likelihood of conflicts is high.
    • Best for systems where data consistency is critical.
    • Example: A financial system where multiple transactions on the same account need to be processed in a strictly serialized manner.

    6. Handling Locking Exceptions#

    OptimisticLockException:#

    • Thrown when optimistic locking detects a version conflict.
    • Can be handled with a retry mechanism.

    PessimisticLockException:#

    • Thrown when a transaction cannot acquire a lock in pessimistic mode.
    • Can be handled by retrying or aborting the transaction.

    In this article, we explored the essential concepts of optimistic and pessimistic locking in JPA, highlighting their roles in managing concurrency in database systems. We discussed the differences between these two locking strategies, including their definitions, use cases, and practical implementations.

    Last updated on Oct 25, 2024