16

我在使用休眠的基于 Spring 框架的 Web 应用程序的控制器中遇到此异常。我尝试了很多方法来解决这个问题,但无法解决。

在控制器的方法中,handleRequestInternal对数据库的调用主要是为了“读取”,除非它是提交操作。我一直在使用 Spring 的 Session 但移到了getHibernateTemplate(),问题仍然存在。

基本上,这是对数据库的第二次调用会引发此异常。那是:

1)getEquipmentsByNumber(number) {首先根据“数字”从数据库中获取设备,该设备具有属性列表,每个属性都有值列表。我遍历这些值(原始对象字符串)以读入变量)

2) getMaterialById(id){根据id获取材料}

我确实理解第二次调用很可能是使会话“刷新”,但我只是在“读取”对象,那么如果没有任何更改,为什么第二次调用会在 Equipment 属性上抛出过时的对象状态异常?

调用后我无法清除缓存,因为它会导致我传递给视图的对象出现 LazyExceptions。

我已阅读此内容: https ://forums.hibernate.org/viewtopic.php?f=1&t=996355&start=0 但根据提供的建议无法解决问题。

我该如何解决这个问题?任何想法和想法都会受到赞赏。

更新: 我刚刚测试的是,在getEquipmentsByNumber()从属性列表中读取变量后的函数中,我这样做:getHibernateTemplate().flush();现在异常在这一行,而不是调用获取材料(即getMaterialById(id))。

更新: 在显式调用刷新之前,我正在从会话缓存中删除对象,以便缓存中没有陈旧的对象。

getHibernateTemplate().evict(equipment);
getHibernateTemplate().flush();

好的,所以现在问题已经转移到我执行此操作后从 DB 获取的下一个问题。我想我必须将这些方法标记为同步并在我读完它们的内容后立即驱逐对象!听起来不太好。

更新: 使handleRequestInternal方法“同步”。错误消失了。当然,不是最好的解决方案,而是怎么做!尝试handleRequestInternal关闭当前会话并打开一个新会话。但这会导致应用程序的其他部分无法正常工作。尝试使用ThreadLocal也没有用。

4

4 回答 4

6

您以某种方式误用了 Hibernate,导致它认为您正在从数据库中更新删除对象。

这就是为什么调用flush()会引发异常。

一种可能性: 您通过 servlet 或控制器的成员字段错误地“共享”会话或实体。这是“同步”会改变您的错误症状的主要原因。简短的解决方案:永远不要这样做。会话和实体不应该也不应该以这种方式工作——每个请求都应该独立处理。

另一种可能性: unsaved-value“int”PK 字段默认为 0。如果您真的想使用 0 作为有效的 PK 值,您可以将它们键入为“整数”。

第三个建议: 明确使用 Hibernate Session,学习编写简单的正确代码,然后为 Hibernate/Spring 库加载 Java 源代码,这样您就可以阅读和理解这些库实际上为您做什么。

于 2011-11-15T05:18:27.840 回答
3

我也一直在与这个异常作斗争,但是当我在对象上加锁时它继续重复出现(在测试环境中,我知道我是唯一接触对象的进程),我决定给出括号在堆栈中跟踪其应有的考虑。

org.hibernate.StaleObjectStateException:行已被另一个事务更新或删除(或未保存的值映射不正确):[com.rc.model.mexp.MerchantAccount#59132]

在我们的例子中,结果证明映射是错误的。我们有type="text"一个字段的映射,它是数据库中的一个 mediumtext 类型,而且 Hibernate 似乎真的很讨厌这种情况,至少在某些情况下是这样。我们从该字段的映射中完全删除了类型规范,问题得到了解决。

现在奇怪的是,在我们的生产环境中,假设存在问题的映射,我们没有得到这个异常。有人知道为什么会这样吗?我们在开发和生产环境中使用相同版本的 MySQL - “5.0.22-log”(我不知道“-log”是什么意思)。

于 2011-09-15T19:56:42.893 回答
2

这里有 3 种可能性(因为我不确切知道您正在使用哪种休眠会话处理)。一个接一个地添加并测试:

在父对象和子对象之间使用双向映射inverse=true,因此父对象或子对象的更改将正确传播到关系的另一端。

使用or列添加对乐观锁定的支持TimeStampVersion

使用连接查询将整个对象图 [父+子] 一起获取,从而完全避免第二次调用。

最后,当且仅当没有任何工作时: 再次加载父级Id(您已经拥有)并填充修改后的数据然后更新。

生活会很好!:)

于 2010-11-05T07:10:17.547 回答
0

这个问题是我经历过的,非常令人沮丧,尽管在你的 DAO/Hibernate 调用中一定有一些奇怪的事情发生,因为如果你通过 ID 进行查找,就没有理由获得陈旧状态,因为这只是对对象的简单查找。

首先,确保你所有的方法都用@Transaction(required=true) // you'll have to look up the exact syntax

但是,当您尝试对已从从中检索它的会话中分离的对象进行更改时,通常会引发此异常。解决这个问题通常并不简单,需要发布更多代码,以便我们可以准确地看到发生了什么;我的一般建议是创建一个@Service在单个事务中执行这些类型的事情

于 2010-07-01T14:51:19.563 回答