10

我在 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()

在 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属性不是 Keyword 对象的主键。

更新:我的数据访问代码:我正在使用HibernateTransactionManager支持线程绑定 Hibernate 会话的 Spring。通过 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()。它们都是读操作。有趣的是,在关键字 select by name 操作之后,会话实际上被标记为脏。实际的代码是这样的:

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

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

4

3 回答 3

14

我相信给出的其他答案是不正确的。访问不存在的行不会给出 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 的问题,即乐观并发检查失败。乐观并发检查失败可能与关键字实体有关(因为它只是将会话中所有更新的实体刷新到数据库)。但是,在您的情况下,它实际上与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])

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

于 2013-01-21T06:27:39.337 回答
1

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

于 2013-01-21T05:10:04.003 回答
0

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

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

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

于 2013-01-21T05:19:33.847 回答