Java 读取操作中休眠的StaleObjectStateException?

Java 读取操作中休眠的StaleObjectStateException?,java,spring,hibernate,spring-integration,Java,Spring,Hibernate,Spring Integration,我在Spring的侦听器中使用HibernateDefaultMessageLisenerContainer 当我让侦听器与多个线程一起运行时,对于只读操作,我经常遇到这样的StaleStateException: Query q = session.createQuery("SELECT k FROM Keyword k WHERE k.name = :name").setParameter("name", keywordName); List<Keyword> kws = q.l

我在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()
这真的很奇怪,因为读取操作应该从DB读取一个新副本,而不是检查版本冲突并抛出
StaleObjectStateException

name
属性不是关键字对象的主键

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

通过将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>
会话在第一次迭代后变脏。(KeywordDao.getKeywordByName实现如上所述)。 有什么想法吗?谢谢

尝试访问不存在的行时,会发生
stalestateException
。检查您的
关键字.getName()
以查看它返回的内容。

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

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


我建议捕获StaleObjectStateException,然后再试一次。

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

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

调用query.list()时,请访问org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)

位于org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1175)
hibernate将确定是否需要自动刷新会话。由于某种原因,Hibernate认为需要自动刷新。(可能是因为您以前在同一个会话中更新了某些关键字实体,或者其他实体……这是我不能诚实地告诉您的)

在org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2805)
然后hibernate将把会话中的所有更改刷新到DB。这里出现了StaleObjectStateException问题,这意味着乐观并发检查失败。乐观并发检查失败可能与关键字实体有关,也可能与关键字实体无关(因为它只是将会话中所有更新的实体刷新到数据库中)。但是,在您的案例中,它实际上与
关键字
实体相关(
原因:org.hibernate.StaleObjectStateException:行被另一个事务更新或删除(或者未保存的值映射不正确):[com.ncs.singtel.aurora.common.model.Keyword#7550]


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

您可以输入完整的数据访问代码吗?您的会话是否跨线程共享?在
关键字
中是否有任何
@PostRead
代码来更新实体的内容,或者在Dao中是否有任何特殊逻辑在检索后更新
关键字
实体?是的,我的错。有一部分要更新关键字。我认为你的问题是正确的。我能够重构代码并删除不必要的更新,从而减少staleobject异常。我不是OP,但据我所知,乐观并发检查发生在您实际执行更新以及将更新刷新到DB时。简单查询不会触发乐观并发检查。@Sublin:谢谢您的回答。我没有问题重试该操作,但我想知道为什么读操作会返回StaleObject。我不在线程之间共享会话对象,所以读取操作应该从DB读取对象,这不应该导致任何过期对象,对吗?我将用数据访问代码更新我的答案。陈旧对象不是由直接读取引起的。请参考我的答案,了解为什么读取会间接导致乐观并发检查感谢您的答案。我正试图找出原因,看看是否可以避免。当前,如果我重试,即使由于重试次数较多而有更多线程,运行时也会保持不变。我已尝试注销会话。在引发此异常之前,在所有DB操作之间检查isDirty()。q.list()。但有趣的是,前面的操作都不会导致会话变脏(它们都是读取的)。我建议您使用
jdbcslog
并转储实际的update语句和绑定变量。这可能会提示您是哪个实体导致了问题。我将您的问题标记为正确。这让我明白了query.list()会在select语句之前由hibernate自动刷新。我认为flush()默认处于手动模式。@AdrianShum,StaleObjectException在Hibernate中不存在。StaleStateException和StaleObjectStateException是相关的(见下文),两者的区别很重要。除此之外,答案很好。没错,当(从API文档)“我们尝试删除或更新不存在的行”时,也会发生StaleStateException,但问题是关于StaleObjectStateException,而不是StaleStateException。不过,它们是相关的。首先,抛出StaleStateException,然后,如果正在更新“版本化”实体,则会捕获该实体,然后最终抛出StaleObjectStateException。所以你的说法是正确的,但严格来说,与t无关
<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>
for (String n : keywordNames) {
    Keyword k = keywordDao.getKeywordByName(n);
}