先將自己出現錯誤的全部代碼都貼出來:
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中的對象的唯一標識符相同的沖突,其根本就是表現在數據庫中的表格中的主鍵值相同的沖突或者其他唯一標識符沖突。解決這個沖突,我們必須知道,到底是哪個唯一標識符沖突,然后再去修改掉相同的標識符,讓他們值不再相同,這樣問題就解決了~