We have a situation where we need to increment the version of an entity when the state of its related entities changes. To do this, we are calling EntityManager#refresh(entity, LockModeType.PESSIMISTIC_FORCE_INCREMENT). We chose to use a pessimistic lock because we need to guarantee that the version is incremented and we want to avoid the transaction rollback that would occur if we used an optimistic lock and it failed. In our testing with MySQL, this approach worked as expected, but when we switched to Oracle, we starting seeing java.sql.SQLException: ORA-00054: resource busy and acquire with NOWAIT specified. Tracing into the hibernate Dialect class, we see the following:
Code:
private String getForUpdateString(LockMode lockMode, int timeout){
switch ( lockMode ) {
case UPGRADE:
return getForUpdateString();
case PESSIMISTIC_READ:
return getReadLockString( timeout );
case PESSIMISTIC_WRITE:
return getWriteLockString( timeout );
case UPGRADE_NOWAIT:
case FORCE:
case PESSIMISTIC_FORCE_INCREMENT:
return getForUpdateNowaitString();
default:
return "";
}
}
This explains the exception - MySQL does not support NOWAIT, so the locking works as we expect but Oracle supports NOWAIT and fails immediately if the lock is already held. The question is, why does the Dialect base class specifically choose to use NOWAIT functionality for PESSIMISTIC_FORCE_INCREMENT? It seems like there must have been a reason when the code was written…
Taking a step back and reevaluating our approach, we're also interested to know if there's a better way to achieve the behavior we need - modifications to a relationship or its members increments the version of the parent. OPTIMISTIC_FORCE_INCREMENT would be perfect - we just want to ensure the version is incremented, even if another thread does it - except for the fact that it throws OptimisticLockException which rolls back our whole transaction!
Right now, we're thinking of extending the Oracle dialect and disabling NOWAIT since we don't want that behavior anyway. It would be nice if the behavior of the getForUpdateString() method in the Dialect base class was configurable. I suppose we could also use a native query to increment the version without locking.
Any suggestions are greatly appreciated!