我使用两种测试方法运行单元测试:一种在H2数据库上创建一个实体,另一种通过某些选择标准找到它,然后删除它。这两种方法都包装了JTA用户事务中的所有数据库交互(每个方法一个)。
现在,在后端发生一些(未知)更改之后,delete方法失败并出现乐观锁定异常:
Caused by: org.hibernate.OptimisticLockException: Newer version [null] of entity [[com.example.entities.MyEntity#10001]] found in database
at org.hibernate.action.internal.EntityVerifyVersionProcess.doBeforeTransactionCompletion(EntityVerifyVersionProcess.java:54)
at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:699)
at org.hibernate.engine.spi.ActionQueue.beforeTransactionCompletion(ActionQueue.java:321)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:613)
at org.hibernate.engine.transaction.synchronization.internal.SynchronizationCallbackCoordinatorImpl.beforeCompletion(SynchronizationCallbackCoordinatorImpl.java:122)
at org.hibernate.engine.transaction.synchronization.internal.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:53)
at bitronix.tm.BitronixTransaction.fireBeforeCompletionEvent(BitronixTransaction.java:532)
at bitronix.tm.BitronixTransaction.commit(BitronixTransaction.java:235)
... 97 more
该实体具有version
属性,该属性使用@Version
进行注释。实体值为0
,并且数据库上实际上没有该实体的更新版本。看起来finder按预期工作(它找到持久化实体)
实际上,验证器找不到“当前版本”。我能够通过hibernate类调试我的方式,直到找到应该获取当前实体的预准备语句(在AbstractEntityPersister中):
public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException {
// ...
try {
PreparedStatement st = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( getVersionSelectString() );
try {
getIdentifierType().nullSafeSet( st, id, 1, session );
ResultSet rs = session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().extract( st );
try {
if ( !rs.next() ) {
return null; // <- that' where I end up. version = null
}
语句正确,id也是正确的,但查询结果为空。
prep68: select version from my_table where my_id =? {1: 10001}
但是现在将版本号0
与null
进行比较,它们不相等并且会引发OptimisticLockException。
非常欢迎任何帮助,提示,想法和解释。
答案 0 :(得分:1)
这似乎是Hibernate中的错误。事务结束时,将再次获取要更改的实体(其中remove()
是其可能的形式),以将数据库版本号与已加载实体的版本号进行比较,以查看是否存在差异。不同之处在于在事务处理期间实体已在数据库中更改,因此它被中止。但是,显然,由于删除了该实体,因此找不到确切的实体。当然,此时仅在实体管理器中将其删除,而删除尚未提交。我不知道这是由于在不应该使用实体管理器的情况下产生的,还是由于删除操作已被刷新并且尽管尚未提交但被认为在该事务内完成的缘故。无论如何,最终结果是将实际版本号与null
进行比较,从而使锁测试失败。
此问题已从Hibernate版本4.3.8和5.0.0.Beta1开始修复。可以在这里找到问题:https://hibernate.atlassian.net/browse/HHH-9419
这是一个古老的问题,但是从被问到解决方案用了一年半的时间。大多数人现在可能正在使用较新的Hibernate版本(或使用当时确实表现出正确行为的EclipseLink),但是有一个项目由于种种原因我被迫使用较旧的版本,并且对此感到st恼。 / p>