java StaleObjectStateException 与 Hibernate 在读操作中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14432813/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 16:14:50  来源:igfitidea点击:

StaleObjectStateException with Hibernate in read operation?

javaspringhibernatespring-integration

提问by Khue Vu

I am using Hibernate in a listener of Spring DefaultMessageLisenerContainer.

我在 Spring 的侦听器中使用 Hibernate DefaultMessageLisenerContainer

When I let the listener run with multiple threads, I often encounter this StaleStateExceptionfor a read only operation:

当我让侦听器以多个线程运行时,我经常遇到这种StaleStateException只读操作:

Query q = session.createQuery("SELECT k FROM Keyword k WHERE k.name = :name").setParameter("name", keywordName);
List<Keyword> kws = q.list()

The exception is thrown at q.list():

在 q.list() 处抛出异常:

optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

乐观锁失败;嵌套异常是 org.hibernate.StaleObjectStateException:行已被另一个事务更新或删除(或未保存的值映射不正确)

Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.aurora.common.model.Keyword#7550]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1934)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2578)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2478)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:114)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:267)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:259)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:179)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:64)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1251)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)

It is really strange as read operation should read a fresh copy from DB rather than check for a version conflict and throw StaleObjectStateException.

这真的很奇怪,因为读取操作应该从数据库读取一个新副本,而不是检查版本冲突并抛出StaleObjectStateException

The nameattribute is not the primary key of Keyword object.

name属性不是 Keyword 对象的主键。

UPDATE: My data access code: I am using Spring's HibernateTransactionManagerwhich support thread-bound Hibernate session. The Hibernate session is retrieved through SessionFactory.getCurrentSession() method.

更新:我的数据访问代码:我正在使用HibernateTransactionManager支持线程绑定 Hibernate 会话的Spring 。Hibernate 会话通过 SessionFactory.getCurrentSession() 方法检索。

Each transaction wrap around a invoke of listener by assigning the HibernateTransactionManager to MessageListenerContainer:

每个事务通过将 HibernateTransactionManager 分配给 MessageListenerContainer 来环绕侦听器的调用:

<jms:listener-container connection-factory="connectionFactory" concurrency="3-3" prefetch="6" transaction-manager="transactionManager">
        <jms:listener destination="${requests}" response-destination="${replies}" ref="chunkHandler" method="handleChunk" />
    </jms:listener-container>

UPDATE :As in the suggested answer, there might be other operations causing staleObjectStateException. I have tried logging out the Session.isDirty(), for all other operations prior to that. They are all read operation. Interestingly, the session is actually marked as dirty after the keyword select by name operation. The actual code is something like this:

更新:与建议的答案一样,可能还有其他操作导致 staleObjectStateException。在此之前,我已尝试注销 Session.isDirty() 以进行所有其他操作。它们都是读操作。有趣的是,在关键字 select by name 操作之后,会话实际上被标记为脏。实际代码是这样的:

for (String n : keywordNames) {
    Keyword k = keywordDao.getKeywordByName(n);
}

The session is dirty after the first iteration. (KeywordDao.getKeywordByName implmentation is as above). Any idea ? Thanks, Khue.

第一次迭代后会话是脏的。(KeywordDao.getKeywordByName 实现如上)。任何的想法 ?谢谢,奎。

回答by Adrian Shum

I believe other answers given are not correct. Accessing row does not exist does not give StaleObjectStateException, and simply query an entity is not going to trigger optimistic lock for that entity too.

我相信给出的其他答案是不正确的。访问不存在的行不会给出 StaleObjectStateException,并且简单地查询实体也不会触发该实体的乐观锁。

Further inspection on the stack trace will give some hints for the cause:

对堆栈跟踪的进一步检查将为原因提供一些提示:

at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)When you are calling query.list()

at org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)当您调用 query.list()

at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)Hibernate will determine if auto flush of the session is required. By some reason Hibernate believe auto flush is required. (Probably due to you have previously done update on some Keyword entity in the same session, or other entities... that's something I cannot tell honestly)

at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)Hibernate 将确定是否需要自动刷新会话。由于某种原因,Hibernate 认为需要自动刷新。(可能是因为您之前在同一会话中对某些 Keyword 实体或其他实体进行了更新……老实说,这是我无法说的)

at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)Then Hibernate will flush all the changes in the session to DB. And, the problem of StaleObjectStateException occurs here, which means Optimistic Concurrency check failure. The optimistic concurrency check failure MAY or MAY NOT relates to Keyword entity (coz it is simply flushing all updated entities in session to DB). However, in your case, it is actually related to Keywordentity ( Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.ncs.singtel.aurora.common.model.Keyword#7550])

at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)然后 Hibernate 会将会话中的所有更改刷新到 DB。并且,这里出现了StaleObjectStateException的问题,表示乐观并发检查失败。乐观并发检查失败可能与关键字实体无关(因为它只是将会话中的所有更新实体刷新到数据库)。但是,在您的情况下,它实际上与Keyword实体 ( Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.ncs.singtel.aurora.common.model.Keyword#7550])

Please verify what is the cause for the optimistic concurrency failure. Normally we simply rethrow the optimistic concurrency exception to caller and let caller decide if they want to invoke the function again. However it all depends on your design.

请验证乐观并发失败的原因是什么。通常我们简单地向调用者重新抛出乐观并发异常,并让调用者决定是否要再次调用该函数。但是,这一切都取决于您的设计。

回答by Ramya

The stalestateExceptionoccurs when we try to access a row that doesn't exist. check your keyword.getName()to see what it returns.

stalestateException当我们尝试访问不存在的行时会发生这种情况。检查你的keyword.getName(),看看它返回什么。

回答by Subin Sebastian

Some other transactions could be updating Keyword entity at the same time as you read and your read operation could result in Stale objects.

其他一些事务可能会在您读取的同时更新 Keyword 实体,并且您的读取操作可能会导致 Stale 对象。

This is optimistic locking. You can consider pessismistic locking , but it will seriously affect the performance.

这就是乐观锁。可以考虑悲观锁,但是会严重影响性能。

I would suggest catch StaleObjectStateExceptionand try to read again.

我建议抓住StaleObjectStateException并尝试再次阅读。