I am trying JPA's support of database locking in order to avoid concurrent requests in a record. In my scenario I need to use a pesssimistic lock. I got that using the following approach:
EntityManager em;
...
Map<String,Object> props = new HashMap<String,Object>();
props.put("javax.persistence.lock.timeout", 0);
em.find(MyEntity.class, id, LockModeType.PESSIMISTIC_READ, props)
but with that approach I handle a LockTimeoutException
instead of a PessimisticLockException
. How to handle a PessimisticLockException
specifically?
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.
Pessimistic locking in hibernatePessimisticLockException will be thrown when we query for rows which are already locked. If the initial select query is successful, rows which meet the select query criteria are locked for the duration of a transaction. We can be sure that no other transaction will modify them.
In order to use optimistic locking, we need to have an entity including a property with @Version annotation. While using it, each transaction that reads data holds the value of the version property. Before the transaction wants to make an update, it checks the version property again.
Using the "test-oracle", "test-mysql" or "test-postgresql" profile for your integration tests will allow you to run all pessimistic locking handling tests against your production database. 🔔 This format is suitable for the local environment when you do changes to your pessimistic locking handling.
According to the spec, the PessimisticLockException
only occurs on a transaction-level rollback:
When the lock cannot be obtained, and the database locking failure results in transaction-level rollback, the provider must throw the PessimisticLockException and ensure that the JTA transaction or EntityTransaction has been marked for rollback.
Your transaction seems to consists only of that single data retrieval query and the persistence provider considers that if a lock error occurs on that query, then the rollback would be considered as a statement-level rollback. Which according to the spec would result in the LockTimeoutException
:
When the lock cannot be obtained, and the database locking failure results in only statement-level rollback, the provider must throw the LockTimeoutException (and must not mark the transaction for rollback).
I think thats a clever way of the driver / persistence provider to give you a chance to repeat / fix your statement instead of rolling back the whole transaction implicitly.
In general, I think that if you had an insert or an update before that find (or some more complex data manipulation statements), then you would get the PessimisticLockException
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With