a different object with the same identifier value was already associated with th

本文详细介绍了在使用Hibernate时遇到的adifferentobjectwiththesameidentifiervaluewasalreadyassociatedwiththesession错误的产生原因及解决方法,包括使用session.clean()、refresh()等操作来临时解决此问题,并特别指出错误常出现在一对多和多对多映射中。

解决a different object with the same identifier value was already associated with the session错误

 

 

这个错误我一共遇到过两次,一直没有找到很好的解决方案,这个错误产生原因相信大家都知道,因为在hibernate中同一个session里面有了两个相同标识但是是不同实体,当这时运行saveOrUpdate(object)操作的时候就会报这个错误。呵呵,也许你会说,你这么说跟没说没什么区别,我承认,呵呵,我不知道具体为什么会产生这个错误,要不然也不会很久都没有解决,现在,给出一个临时的解决方案,给向我一样,没有办法找到根源的人一个能够继续执行下去的方法(当然是对的,只是不是从产生原因入手)

其实要解决这个问题很简单,只需要进行session.clean()操作就可以解决了,但是你在clean操作后面又进行了saveOrUpdate(object)操作,有可能会报出"Found two representations of same collection",我找了很多资料,没有什么很好的解释,其中这篇文章帮助最大http://opensource.atlassian.com/projects/hibernate/browse/HHH-509。

最后通过session.refresh(object)方法就可以解决了,注意,当object不是数据库中已有数据的对象的时候,不能使用session.refresh(object)因为refresh是从hibernate的session中去重新取object,如果session中没有这个对象,则会报错所以当你使用saveOrUpdate(object)之前还需要判断一下

当然这个问题最容易解决的办法还是使用Hibernate里面自带的merge()方法。不过我始终觉得碰到问题就用这种软件自带的非常用方法(和saveOrUpdate(),save(),update()相比)感觉十分不爽。

后来我还发现这种错误经常出现在一对多映射和多对多映射,请大家在使用一对多和多对多映射的时候要小心一些


Category是树形结构,以下是实现修改功能的代码,一开始,我用了getHibernateTemplate().update()

结果出现a different object with the same identifier value was already associated with the session

,上网查了查,使用了getSession().flush();
  getSession().clear();

之后,另外一个问题出现了:Found two representations of same collection

于是乎,继续找办法解决,最后发现getHibernateTemplate().merge(category);

可以彻底解决问题,然而原理到底是如何,却是弄不明白。

//修改category
    public void updateCategory(Category category) {        
        
        //按照传进来的category,先把原有的category读取出来
        Category cc=(Category)getHibernateTemplate()
                    .get(Category.class, category.getId());

        //把原有的孩子拿到
        category.setChildren(cc.getChildren());

        //把原有的父亲拿到
        category.setParent(cc.getParent());
        
        //保存修改
                
        getSession().flush();
        getSession().clear();
        //不要用update()方法
        getHibernateTemplate().merge(category);

    }

 

 

 

<转自:http://winter8.iteye.com/blog/558071>

在使用如 Hibernate 或 JPA 等 ORM(对象关系映射)框架进行开发时,开发者常常会遇到对象标识冲突的错误: > **"a different object with the same identifier value was already associated"** 这个错误通常出现在持久化上下文中(Persistence Context),当同一个实体标识符(ID)被两个不同的实体实例所引用时发生。这会导致框架无法确定应该以哪一个实例为准,从而抛出异常。 ### 原因分析 1. **重复关联相同 ID 的不同实体实例** 在一个 `EntityManager` 作用域内,如果尝试将两个具有相同 ID 的不同实体对象关联到持久化上下文,就会触发该错误。例如,在更新或保存操作中添加了已存在的实体 ID 对象。 2. **合并(merge)与托管状态(managed entity)冲突** 当调用 `entityManager.merge()` 方法时,如果当前持久化上下文中已经存在一个相同 ID 的实体,并且该实体处于“托管”状态(managed),则可能导致冲突[^1]。 3. **缓存中的遗留实体** 如果一级缓存(Persistence Context)或二级缓存中已经存在某个实体,而后续又试图加载或创建相同 ID 的新实例,也可能引发此问题。 --- ### 解决方案 #### 1. 避免手动创建多个相同 ID 的实体实例 确保不要人为构造两个拥有相同主键值的不同实体对象并将其同时加入到持久化上下文中。例如: ```java User user1 = new User(); user1.setId(1L); user1.setName("Alice"); User user2 = new User(); user2.setId(1L); // 相同 ID user2.setName("Bob"); entityManager.merge(user1); entityManager.merge(user2); // 抛出异常 ``` 应改为只操作一个实体,或先清除已有实体。 #### 2. 使用 `EntityManager.detach()` 分离旧实体 如果确实需要替换现有实体,可以在操作前调用 `detach()` 方法将旧实体从上下文中移除: ```java User existing = entityManager.find(User.class, 1L); if (existing != null) { entityManager.detach(existing); } User newUser = new User(); newUser.setId(1L); newUser.setName("New Name"); entityManager.merge(newUser); ``` #### 3. 清除整个持久化上下文 在某些复杂场景下,可以考虑清空整个上下文来避免冲突: ```java entityManager.clear(); ``` 但要注意这种方式会影响所有当前正在管理的实体状态。 #### 4. 检查是否重复添加实体到集合中 在级联操作中,如果多个父实体引用了相同的子实体对象,也会导致冲突。应确保每个子实体仅被一个父实体引用,或使用 `@EqualsAndHashCode(of = "id")` 和 `equals()`/`hashCode()` 正确实现。 #### 5. 使用 DTO 转换再映射 对于更新操作,建议使用 DTO(数据传输对象)接收前端输入,然后将 DTO 数据复制到已加载的实体对象中,而不是直接操作实体: ```java User existing = entityManager.find(User.class, dto.getId()); existing.setName(dto.getName()); // 更新其他字段... entityManager.merge(existing); ``` 这样可以确保操作的是已托管的实体,避免重复绑定。 --- ### 总结 此类错误本质上是由于 ORM 框架的“同一性保证”机制所引起的。为了避免这类问题,应合理管理实体生命周期、避免重复绑定相同 ID 的不同对象,并根据业务需求选择合适的操作方式(如 detach、clear、merge 或重新加载实体)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值