(转)解决a different object with the same identifier value was already associated with the session错误
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")的语句不会同步数据库中的表。
(转)hibernate中一种导致a different object with the same identifier value was already associated with the session错误方式及解决方法
先将自己出现错误的全部代码都贴出来:
hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.password">tiger</property> <property name="hibernate.connection.url">jdbc:mysql:///hibernate?useUnicode=true&characterEncoding=UTF-8</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="hbm2ddl.auto">create</property> <mapping resource="com/third/Dao1/Grader1.hbm.xml"/> <mapping resource="com/third/Dao1/Students1.hbm.xml"/> </session-factory> </hibernate-configuration>
Students1.java
package com.third.Dao1; import java.io.Serializable; public class Students1 implements Serializable { private int sid; private String sname; private String sgender; public Students1() { } public int getSid() { return sid; } public void setSid(int sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public String getSgender() { return sgender; } public void setSgender(String sgender) { this.sgender = sgender; } }
Grader1.java
package com.third.Dao1; import java.io.Serializable; import java.util.HashSet; import java.util.Set; public class Grader1 implements Serializable { private int gid; private String gname; private Set<Students1> stuSet=new HashSet<Students1>(); public Grader1() { } public int getGid() { return gid; } public void setGid(int gid) { this.gid = gid; } public String getGname() { return gname; } public void setGname(String gname) { this.gname = gname; } public Set<Students1> getStuSet() { return stuSet; } public void setStuSet(Set<Students1> stuSet) { this.stuSet = stuSet; } }
由eclipse帮助生成的hbm.xml文件:
Students1.hbm.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 20:42:49 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao1.Students1" table="STUDENTS1"> <id name="sid" type="int"> <column name="SID" /> <generator class="assigned" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" /> </property> <property name="sgender" type="java.lang.String"> <column name="SGENDER" /> </property> </class> </hibernate-mapping>
Grader1.cfg.xml
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 20:42:49 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao1.Grader1" table="GRADER1"> <id name="gid" type="int"> <column name="GID" /> <generator class="increment" /> </id> <property name="gname" type="java.lang.String"> <column name="GNAME" /> </property> <set name="stuSet" inverse="false" table="STUDENTS1" lazy="true"> <key> <column name="GID" /> </key> <one-to-many class="com.third.Dao1.Students1" /> </set> </class> </hibernate-mapping>
测试文件:
Test.java
1 package com.third; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.hibernate.cfg.Configuration; 10 import org.hibernate.service.ServiceRegistry; 11 import org.hibernate.service.ServiceRegistryBuilder; 12 import org.junit.After; 13 import org.junit.Before; 14 import org.junit.Test; 15 16 import com.third.Dao1.Grader1; 17 import com.third.Dao1.Students1; 18 19 /*import com.third.Dao.Grader; 20 import com.third.Dao.Students;*/ 21 public class Test1 { 22 23 private static SessionFactory sessionFactory; 24 private static Session session; 25 private static Transaction transaction; 26 @Before 27 public void init(){ 28 //创建配置对象,匹配读取hibernate.cfg.xml文件 29 Configuration config=new Configuration().configure(); 30 //获取服务注册对象(该对象中封装了hibernate.cfg.xml文件中的<properties>、<mapping>信息) 31 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder() 32 .applySettings(config.getProperties()).buildServiceRegistry(); 33 //获取sessionFactory对象(该对象中通过传参serviceRegistry对象,获取了<mapping><properties>信息) 34 sessionFactory=config.buildSessionFactory(serviceRegistry); 35 } 55 56 @Test 57 public void test2(){ 58 //通过sessionFactory对象获取session对象 59 session=sessionFactory.openSession(); 60 //通过session对象开启事务,并返回Transaction对象 61 transaction=session.beginTransaction(); 62 63 //创建students和Grader对象 64 Students1 student2=new Students1(); 65 student2.setSname("小美"); 66 student2.setSgender("女"); 67 Students1 student3=new Students1(); 68 student3.setSname("小宏"); 69 student3.setSgender("女"); 70 71 Set<Students1> stuSet=new HashSet<Students1>(); 72 stuSet.add(student2); 73 stuSet.add(student3); 74 75 //实例Grader对象 76 Grader1 grader2=new Grader1(); 77 grader2.setGname("信息与计算科学二班"); 78 grader2.setStuSet(stuSet);; 79 80 81 session.save(student2); 82 83 session.save(student3); 84 session.save(grader2); 85 } 86 @After 87 public void destory(){ 88 //提交事务 89 transaction.commit(); 90 //关闭资源 91 if(session!=null){ 92 session.close(); 93 } 94 if(sessionFactory!=null){ 95 sessionFactory.close(); 96 } 97 } 98 }
下面是报错的报文:
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.third.Dao1.Students1#0] at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:179) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:206) at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:191) at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49) at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90) at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:764) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:756) at org.hibernate.internal.SessionImpl.save(SessionImpl.java:752) at com.third.Test1.test2(Test1.java:83) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
原因分析:
第一步:
报错报文中我们重点看加红的两行信息,第一行:
a different object with the same identifier value was already associated with the session: [com.third.Dao1.Students1#0]
大致的意思说:一个不同的Object对象和session:[com.third.Dao1.Students1#0]中已有的一个对象拥有着相同的唯一辨识符。我们仔细看这段代码,这段代码的主要作用是向Mysql数据库中创建两个关联表格,并且向表中添加一些信息。我们在分析一下这个报错是在我们编写的哪行代码执行不下去了之后,然后抛出异常。
第二步:
很明显:at com.third.Test1.test2(Test1.java:83)
这行错误报文指出我们是在Test1.java代码的83行抛出的异常,我们找到这行代码:session.save(student3);分析这行代码具体执行情况,在这之前只有session.save(students2);代码对session:[com.third.Dao1.Students1#0]操作,这样我们就将这两行代码结合在一起分析。之前说报错的原因是:session:[com.third.Dao1.Students1#0]中已有的对象的唯一标识符和新添加的对象相同,从而产生冲突。而session:[com.third.Dao1.Students1#0]中之前只有代码session.save(students2);对它进行了操作,也就是session:[com.third.Dao1.Students1#0]中只添加了studnets2这一个对象,这样要新添加的对象students3就和students2对象在唯一标识符上冲突。
第三步:
我们打开Navicat for MySQL软件,查看已经由eclipse创建的表格students1的表格结构(通过点击students1表格右键,选择设计表)
很明显表格students1记录的主键是SID,即在session:[com.third.Dao1.Students1#0]中的唯一表示符就是sid,那么我们看看表格中已有的students2的对象记录的主键:
我们能够看到students1表格中有一条SID为0的SNAME为小美的记录,我们可以知道session:[com.third.Dao1.Students1#0]中students2对象的唯一标识符sid为0,而students3对象唯一标识符和它相同冲突,这样students3对象的唯一表示符也将是sid为0。
第四步:
然后我们需要思考,为什么两个对象的唯一标识符都为0,这样我们就需要回到Test1.java代码中去分析,看看对于这个唯一标识符是如何设置的,即查看students2.setSid()和students3.setSid()函数的调用赋值情况。我们在查看完@Test代码段的代码发现,整段代码没有调用students2.setSid()和students3.setSid()函数,当时在设计这个程序时,想法是将主键SID交由MySQL数据库自行递增。所以自己写的代码段就没有对sid进行set赋值。但是MySQL数据库并没有完成主键SID的自动赋值(之前说students2和students3对象的唯一标识符sid都为0,在数据库表格中表现为主键SID都为0),这里就需要讨论数据库的主键的生成策略,而主键的生成策略是由.hbm.xml文件中指定的,这样我们就需要分析Students1.hbm.xml代码,其中控制主键生成策略的是<generator class="" />标签,我们很快找到了这个标签,查看这个标签主键生成策略的设置情况:
我们能看到这里设置的主键生成策略是assigned,而assigned适用于自然主键,由Java应用程序负责生成标识符,也就是说需要我们手动使用setSid()函数手动赋值。而我们并没有进行手动set赋值,而MySQL数据库在assirned主键生成策略下,如果不进行手动set赋值,其主键SID的值将会采取默认的值0,这就解释了唯一标识符sid都是0的冲突问题。
解决方法:(给出两种)
1、修改Students1.hbm.xml代码,其他代码不需要改变:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Generated 2017-3-1 20:42:49 by Hibernate Tools 3.5.0.Final --> <hibernate-mapping> <class name="com.third.Dao1.Students1" table="STUDENTS1"> <id name="sid" type="int"> <column name="SID" /> <generator class="increment" /> </id> <property name="sname" type="java.lang.String"> <column name="SNAME" /> </property> <property name="sgender" type="java.lang.String"> <column name="SGENDER" /> </property> </class> </hibernate-mapping>
修改的代码是绿色背景的代码,将主键生成策略改成increment(适用于代理主键,由hibernate自动递增方式生成)或者改成native(适用于代理主键,根据底层数据库对自动生成标识符的方式自动选择,其中MySQL是自动递增方式生成)。
2、修改Test1.java代码,其他代码不需要改动。
1 package com.third; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.hibernate.cfg.Configuration; 10 import org.hibernate.service.ServiceRegistry; 11 import org.hibernate.service.ServiceRegistryBuilder; 12 import org.junit.After; 13 import org.junit.Before; 14 import org.junit.Test; 15 16 import com.third.Dao1.Grader1; 17 import com.third.Dao1.Students1; 18 19 /*import com.third.Dao.Grader; 20 import com.third.Dao.Students;*/ 21 public class Test1 { 22 23 private static SessionFactory sessionFactory; 24 private static Session session; 25 private static Transaction transaction; 26 @Before 27 public void init(){ 28 //创建配置对象,匹配读取hibernate.cfg.xml文件 29 Configuration config=new Configuration().configure(); 30 //获取服务注册对象(该对象中封装了hibernate.cfg.xml文件中的<properties>、<mapping>信息) 31 ServiceRegistry serviceRegistry=new ServiceRegistryBuilder() 32 .applySettings(config.getProperties()).buildServiceRegistry(); 33 //获取sessionFactory对象(该对象中通过传参serviceRegistry对象,获取了<mapping><properties>信息) 34 sessionFactory=config.buildSessionFactory(serviceRegistry); 35 } 36 37 38 @Test 39 public void test2(){ 40 //通过sessionFactory对象获取session对象 41 session=sessionFactory.openSession(); 42 //通过session对象开启事务,并返回Transaction对象 43 transaction=session.beginTransaction(); 44 45 //创建students和Grader对象 46 Students1 student2=new Students1(); 47 student2.setSname("小美"); 48 student2.setSgender("女"); 49 student2.setSid(1); 50 Students1 student3=new Students1(); 51 student3.setSname("小宏"); 52 student3.setSgender("女"); 53 student3.setSid(2); 54 55 Set<Students1> stuSet=new HashSet<Students1>(); 56 stuSet.add(student2); 57 stuSet.add(student3); 58 59 //实例Grader对象 60 Grader1 grader2=new Grader1(); 61 grader2.setGname("信息与计算科学二班"); 62 grader2.setStuSet(stuSet);; 63 64 65 session.save(student2); 66 67 session.save(student3); 68 session.save(grader2); 69 } 70 @After 71 public void destory(){ 72 //提交事务 73 transaction.commit(); 74 //关闭资源 75 if(session!=null){ 76 session.close(); 77 } 78 if(sessionFactory!=null){ 79 sessionFactory.close(); 80 } 81 } 82 }
添加绿色背景的两段代码,手动的添加唯一标识符的值。
总结:
出现a different object with the same identifier value was already associated with the session的错误的原因是session中的对象的唯一标识符相同的冲突,其根本就是表现在数据库中的表格中的主键值相同的冲突或者其他唯一标识符冲突。解决这个冲突,我们必须知道,到底是哪个唯一标识符冲突,然后再去修改掉相同的标识符,让他们值不再相同,这样问题就解决了~