liveBook · Manning

Shared lock

As explained in Java Persistence with Spring Data and Hibernate epub

A shared lock, or shared read lock, permits compatible reads by multiple transactions while preventing conflicting writes.

A shared lock, also called a shared read lock, allows compatible transactions to read the same rows while preventing conflicting writes. Under read committed isolation, shared read locks support concurrent reads while an uncommitted write blocks access to the row. section 11.2.1

Shared-lock behavior depends on the isolation level and database implementation:

  • Read committed can use shared read locks and exclusive write locks; dirty reads and lost updates are prevented, but unrepeatable and phantom reads may occur. section 11.2.1
  • Repeatable read allows multiple read locks but blocks writes until the readers finish; phantom reads may still occur. section 11.2.1
  • Lock escalation and exact semantics vary by database, so the database dialect and documentation determine operational behavior. section 11.2.1

Requesting a shared lock with JPA

JPA expresses a pessimistic shared lock with LockModeType.PESSIMISTIC_READ. Hibernate can translate this to PostgreSQL's FOR SHARE or MySQL's LOCK IN SHARE MODE; H2 uses FOR UPDATE because it supports only an exclusive lock type. The lock lasts for the database transaction, and failure to obtain it raises an exception. section 11.2.3

Listing 11.6 Locking data pessimistically
Path: /Ch11/transactions/src/test/java/com/manning/javapersistence/ch11
 /concurrency/Locking.java – pessimisticReadWrite()
 
   EntityManager em = emf.createEntityManager();
   em.getTransaction().begin();
 
BigDecimal totalPrice = BigDecimal.ZERO;
for (Long categoryId : CATEGORIES) {
    List<Item> items =                                           #1
        em.createQuery("select i from Item i where i.category.id = :catId", 
         Item.class)
            .setLockMode(LockModeType.PESSIMISTIC_READ)
            .setHint("javax.persistence.lock.timeout", 5000)
            .setParameter("catId", categoryId)
            .getResultList();
    for (Item item : items)                                      #2
        totalPrice = totalPrice.add(item.getBuyNowPrice());
    // . . .  Another transaction tries to obtain a lock and fails
}
em.getTransaction().commit();                                    #3
em.close();
assertEquals(0, totalPrice.compareTo(new BigDecimal("108")));
#1 For each <kbd>Category</kbd>, query all <kbd>Item</kbd> instances in <kbd>PESSIMISTIC_READ</kbd> lock mode. Hibernate locks the rows in the database with the SQL query. If another transaction holds a conflicting lock, wait 5 seconds if possible. If the lock can’t be obtained, the query throws an exception.
#2 If the query returns successfully, we know that we hold an exclusive lock on the data, and no other transaction can access it with an exclusive lock or modify it until this transaction commits.
#3 The locks are released after committing, when the transaction completes.

Shows a JPA pessimistic-read query with a five-second lock timeout inside an explicit transaction. section 11.2.3

How Java Persistence with Spring Data and Hibernate epub covers it

How deep it goes
Explains isolation-level behavior and JPA-to-SQL translation, but defers vendor-specific locking details to database documentation.
Examples
Uses PostgreSQL FOR SHARE, MySQL LOCK IN SHARE MODE, H2 FOR UPDATE, and a JPA pessimistic-read query with a timeout.
What it assumes
Assumes transactions, database isolation levels, JPA lock modes, and database dialects as the surrounding concepts.
What you can do after
Apply via JPA by selecting PESSIMISTIC_READ, configuring a lock timeout, and completing the operation within one transaction.
How it connects
Connects transaction isolation, exclusive locks, pessimistic locking, optimistic locking, and database dialects.

This explainer was generated by Manning's knowledge graph from the text of Java Persistence with Spring Data and Hibernate epub. The section links point to the parts of the book it draws on.