1

我使用两种测试方法运行单元测试:一种在 H2 数据库上创建实体,另一种通过某些选择标准找到它,然后将其删除。这两种方法都将所有数据库交互包装在 JTA 用户事务中(每个方法一个)。

现在在后端进行一些(未知)更改后,删除方法失败并出现乐观锁异常:

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 看起来像预期的那样工作(它找到了持久化的实体)

实际上,验证器没有找到“当前版本”。我能够通过休眠类调试我的方式,直到找到应该获取当前实体的准备好的语句(在 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。

非常欢迎任何帮助、提示、想法和解释。

4

1 回答 1

2

看起来这是 Hibernate 中的一个错误。当事务结束时,被更改的实体(remove()可能是其中的一种形式)被再次获取,以将数据库版本号与加载的实体的版本号进行比较,看看是否存在差异。差异意味着实体在事务期间已在数据库中更改,因此被中止。但显然,由于被移除,该实体将无法准确找到。当然,此时它仅在实体管理器中被删除,删除尚未提交。我不知道这是否是由于在不应该使用实体管理器的情况下使用的结果,还是由于删除已被刷新,尽管尚未提交,但被认为在该事务中完成。任何状况之下,null从而未能通过锁定测试。

这已从 Hibernate 版本 4.3.8 和 5.0.0.Beta1 开始修复。该问题可以在这里找到:https ://hibernate.atlassian.net/browse/HHH-9419

这是一个古老的问题,但从提出要求到提供修复程序花了一年半的时间。大多数人现在可能正在使用较新的 Hibernate 版本(或者使用当时确实表现出正确行为的 EclipseLink),但是有一个项目,由于遗留原因,我被迫使用旧版本并且只是被这个刺痛了。

于 2018-11-28T14:09:17.357 回答