Hi,
we recently updated Hibernate from 3.x to latest 4.2.0 and started to experience problem in code deleting entities with @Version column.
Entity class (annotations for id generation are omitted):
Code:
@Entity
public class TestLock {
@Column(name = "testLockId") @Id
protected Long testLockId;
@Version
protected long version;
}
Problematic transaction (getting EntityManager, transaction begin() and commit() are omitted):
Code:
TestLock testLock = manager.getReference(TestLock.class, testLockId);
manager.lock(testLock, LockModeType.WRITE);
manager.remove(testLock);
Stacktrace:
Code:
javax.persistence.OptimisticLockException
at org.hibernate.ejb.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1413)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1329)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1310)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:80)
at testlockcase.TestLockCase.main(TestLockCase.java:34)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [testlockcase.entity.TestLock#10100]
at org.hibernate.persister.entity.AbstractEntityPersister.forceVersionIncrement(AbstractEntityPersister.java:1835)
at org.hibernate.action.internal.EntityIncrementVersionProcess.doBeforeTransactionCompletion(EntityIncrementVersionProcess.java:53)
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:662)
at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:307)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:612)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:105)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:75)
... 1 more
While this example might look like a nonsense, in the real world there are things to be done between lock() and remove() and it makes sense to lock the entity.
Any ideas what might be wrong?
It looks like a bug to me, detailed logs suggest that Hibernate throws OptimisticLockException after executing query like this:
update TestLock set version=#VERSION + 1 where version=#VERSION and testLockId=#ID
which returns zero results (because the row is deleted already) - this query should not be executed after remove(), right?
Edit: This behaviour is present at least from 4.0.0.Final, as a temporary solution we are forcing PESSIMISTIC locks in our EntityManagerProxy.