在读操作中使用Hibernate的StaleObjectStateException?

Khu*_* Vu 8 java spring hibernate spring-integration

我在Spring的监听器中使用Hibernate DefaultMessageLisenerContainer.

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

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

q.list()抛出异常:

乐观锁定失败; 嵌套异常是org.hibernate.StaleObjectStateException:Row被另一个事务更新或删除(或者unsaved-value映射不正确)

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)
Run Code Online (Sandbox Code Playgroud)

这很奇怪,因为读取操作应该从DB读取新副本而不是检查版本冲突并抛出StaleObjectStateException.

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

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

通过将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>
Run Code Online (Sandbox Code Playgroud)

更新: 与建议的答案一样,可能还有其他操作导致staleObjectStateException.我已经尝试注销Session.isDirty(),用于之前的所有其他操作.它们都是读操作.有趣的是,在关键字按名称操作选择之后,会话实际上被标记为脏.实际代码是这样的:

for (String n : keywordNames) {
    Keyword k = keywordDao.getKeywordByName(n);
}
Run Code Online (Sandbox Code Playgroud)

第一次迭代后会话变脏.(KeywordDao.getKeywordByName implmentation如上).任何的想法 ?谢谢,Khue.

Adr*_*hum 9

我相信其他答案是不正确的.访问行不存在不会产生StaleObjectStateException,并且只是查询实体也不会触发该实体的乐观锁定.

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

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

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

at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)然后Hibernate会将会话中的所有更改刷新到DB.并且,此处发生StaleObjectStateException问题,这意味着Optimistic Concurrency检查失败.乐观并发检查失败可能或可能与关键字实体无关(因为它只是将会话中所有更新的实体刷新到DB).但是,在您的情况下,它实际上与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])相关

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