我在Spring DefaultMessageLisenerContainer
的监听器中使用Hibernate。
当我让侦听器以多个线程运行时,我经常遇到这个StaleStateException
进行只读操作:
Query q = session.createQuery("SELECT k FROM Keyword k WHERE k.name = :name").setParameter("name", keywordName);
List<Keyword> kws = q.list()
q.list()抛出异常:
乐观锁定失败;嵌套异常是 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)
这很奇怪,因为读取操作应该从DB读取新副本而不是检查版本冲突并抛出StaleObjectStateException
。
name
属性不是关键字对象的主键。
更新:
我的数据访问代码:我正在使用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>
更新: 与建议的答案一样,可能还有其他操作导致staleObjectStateException。 我已经尝试注销Session.isDirty(),用于之前的所有其他操作。它们都是读操作。有趣的是,在关键字按名称操作选择之后,会话实际上被标记为脏。实际代码是这样的:
for (String n : keywordNames) {
Keyword k = keywordDao.getKeywordByName(n);
}
第一次迭代后会话变脏。 (KeywordDao.getKeywordByName implmentation如上)。 任何的想法 ?谢谢, 奎。
答案 0 :(得分:8)
我相信其他答案是不正确的。访问行不存在不会给出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]
)
请验证乐观并发失败的原因是什么。通常我们只是向调用者重新抛出乐观并发异常,让调用者决定是否要再次调用该函数。但这一切都取决于你的设计。
答案 1 :(得分:0)
当我们尝试访问不存在的行时,会发生stalestateException
。检查您的keyword.getName()
以查看它返回的内容。
答案 2 :(得分:0)
其他一些事务可能会在您阅读的同时更新关键字实体,并且您的读取操作可能会导致过时对象。
这是乐观的锁定。您可以考虑使用悲观锁定,但这会严重影响性能。
我建议抓住StaleObjectStateException
并尝试再次阅读。