解决a different object with the same identifier value was already associated with the session错误
问题场景:
(我要做对象修改,然后保存进数据库)
我通过数据库获取一个原对象obj;
然后新增了一个修改对象obj1;
然后将obj1.setId(obj.getId());
然后调用数据库实例对象SESSION.UPDATE(OBJ1);
数据库报错,抛出了异常a different object with the same identifier value was already associated with the session。
原因就是session再检测实例化对象时,发现obj和obj1的ID相同(通过地址去检测),导致抛出异常,根据以下解决方法:
buildDevelopers_tDAO.getSessionFactory().getCurrentSession().merge(buildDev_t_after);
即 使用MERGE()方法,而不是使用update()方法;
另外一种解决方法:
不要使用set()方法去赋ID;
obj = obj1;
obj.setName()
.....(根据相应的修改,去进行赋值)
然后调用数据库实例对象SESSION.UPDATE(OBJ1);
即不要使用set方法赋ID即可。
解决方案:
1、a different object with the same identifier value was already associated with the session。
错误原因:在hibernate中同一个session里面有了两个相同标识但是是不同实体。
解决方法一:session.clean()
PS:如果在clean操作后面又进行了saveOrUpdate(object)等改变数据状态的操作,有可能会报出"Found two representations of same collection"异常。
解决方法二:session.refresh(object)
PS:当object不是数据库中已有数据的对象的时候,不能使用session.refresh(object)因为该方法是从hibernate的session中去重新取object,如果session中没有这个对象,则会报错所以当你使用saveOrUpdate(object)之前还需要判断一下。
解决方法三:session.merge(object)
PS:Hibernate里面自带的方法,推荐使用。
2、Found two representations of same collection
错误原因:见1。
解决方法:session.merge(object)
以上两中异常经常出现在一对多映射和多对多映射中
3、问题情况:使用hibernate来进行对对象的保存操作时,出现了exception,导致数据保存不成功,具体报错如是:
Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
使用情况(本人遇到):如果是在保存对象的时候,如果对象有外键,则在保存的时候,要根据外键来获取数据库所对应的记录的对象,之后再set入要保存的的对象中,如果是直接new一个外键的对象(外键的值在数据库有对应的值)再set入保存的对象中的时候,就会报错:Exception in thread "main" org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:,这个是因为,在保存的时候,session会检查在内存的外简单的对象跟你new的对象是否一样(不是根据对象的值,而是根据在内存的地址是否一致来比较的),所以new出来的对象是无法跟内存里面的对象一致的,因而,会说不同的对象有一样的id,所以保存数据库的时候就直接根据外键来获取对应的数据库记录来保存对象,所以这个时候用merge方法还是解决不了问题的。
最后是在查了一下,采用session.merge (object c)代替session.save(object c),即可解决,主要涉及到session.merge ()方法的使用以及session.merge ()跟session.save()、session.update ()方法的区别,这篇博文详细说明了问题....
该方法将修改表中记录,其所需要的实体状态为脱管状态,但是注意,它并不影响调用方法前后的状态,也即该实体依然是脱管状,见例6.4。
例6.4:session.merge ()方法对状态的变化
public void run() {
//创建UserInfo实例
UserInfo userInfo = new UserInfo();
//使之成为脱管状态
userInfo.setId(11112);
userInfo.setName("RW3");
userInfo.setSex("M");
//创建UserInfo实例
UserInfo userInfo2 = new UserInfo();
//使之成为脱管状态
userInfo2.setId(11112);
userInfo2.setName("RW4");
userInfo2.setSex("F");
//启动Session
Session session = HibernateSessionFactory.currentSession();
//启动事务
Transaction tx = session.beginTransaction();
//调用merge方法,此时UserInfo实体状态并没有被持久化
session.merge(userInfo);
//调用merge方法,此时UserInfo实体状态并没有被持久化
//但是数据库中的记录被更新了
①session.merge(userInfo2);
//merge方法与update方法的差别在于针对同样的操作update方法会报错
//原因在于update方法使得实体状态成为了持久化状态,而Session中不允许两个持久化实体有同样的持久化标识
②//session.update(userInfo);
//session.update(userInfo2);
//以下两句不会发送SQL,因为userInfo2不是持久化状态的实体
③userInfo2.setName("RW5");
userInfo2.setSex("M");
//提交事务
tx.commit();
//关闭Hibernate Session
HibernateSessionFactory.closeSession();
}
针对该段代码将执行如下SQL语句:
Hibernate:
/* ①session.merge(userInfo2)的动作 */
select
userinfo0_.id as id0_0_,
userinfo0_.NAME as NAME0_0_,
userinfo0_.SEX as SEX0_0_,
userinfo0_.roomid as roomid0_0_
from
userinfo userinfo0_
where
userinfo0_.id=?
Hibernate:
/* ①session.merge(userInfo2)的动作 */
update
userinfo
set
NAME=?,
SEX=?,
roomid=?
where
id=?
session.merge()方法会首先发送一句select语句,去数据库端获取UserInfo持久化标识所对应的表记录;然后自动生成一个持久化状态的UserInfo实体,与脱管状态的UserInfo实体做比较是否有所改变;一旦发生了改变,才会发送update语句执行更新。而按执行顺序,若两句session.merge()方法针对同一个脱管状态的UserInfo实体,那其结果只会执行最后一个session.merge()方法所发出的update语句。即使执行了session.merge()方法,UserInfo实体依然是脱管状态,因此③userInfo2. setName("RW5")的语句不会同步数据库中的表。