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
