轉載請標明出處 http://www.cnblogs.com/haozhengfei/p/6049276.html html
首先說一下inverse:java
"inverse" 直譯過來就是"反轉,使顛倒"的意思,書面化的解釋爲"是否將關係維護的權力交給對方"mysql
1. 在hibernate中inverse默認是false,也就是己方擁有維護關係的權利, 固然爲true的話,就是把維護關係的權利交給了對方sql
2. 在一對多的關係(多對一)中,一般將一端的inverse設置爲false(一端設爲true的話會多出更新語句,有性能問題,下面會講到),而多對多的關係中,inverse的值只能有一個爲true,由於若是雙發都爲true,那麼雙方都去維護關係,會形成中間關係表中出現重複的數據。(這一點之後有深入的理解,會補上解釋的額)數據庫
說到這裏,那麼問題來了,什麼是關係?關係的具體體現又是什麼?session
什麼是關係?app
"關係"就是兩個表之間的關係,一般爲"一對多","一對一","多對多"三種關係,eclipse
關係的具體體現是什麼?ide
暫且先看下面這張圖,後面會詳細講解性能
這是AClazz.hbm.xml中的部分截圖,在一端配置外鍵關係時,關係的具體體現就是column="clazzid",未來這一列會在學生表中生成(外鍵)
接下來談一談cascade:
1. "cascade"-直譯過來就是"級聯、串聯"的意思,書面化的解釋爲"該屬性會使咱們在操做主對象時,同時Hibernate幫助咱們完成從屬對象 相應的操做
(好比,有Customer和Order這兩張表,關係爲一對多,只使用JDBC刪除Customer表中的一行記錄時,咱們還須要手動的將 Order表中與之關聯的記錄全都刪除,使用Hibernate的'cascade'屬性後,當咱們刪除一條Customer記錄時,Hibernate 會幫助咱們完成相應Order表記錄的刪除工做,方便了咱們的工做)"。
2. 用"cascade"屬性時,主對象(一 方)通常設置爲"all",而多方不建議設置包含delete操做的選項,建議設置多方爲"save-update",這是由於你刪除一方,多方已經沒有 存在的意義了,而刪除多方不能表明一方沒意義了(例如,教室和學生)
舉個例子
好比: AClazz.hbm.xml -->一端
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 3 <!-- 4 hbm.xml的做用 5 將pojo中每個屬性對應到表的列名 6 --> 7 <hibernate-mapping> 8 <class name="com.bjsxt.hibernate.pojo.AClazz" table="a_clazz"> 9 <!-- id必須寫name --> 10 <id name="id"> 11 <generator class="native"/> 12 </id> 13 <property name="name"/> 14 <property name="createTime"/> 15 <!-- 一對多如何設置 --> 16 <set name="studentSet" inverse="false" cascade="all" fetch="subselect"> 17 <!-- 配置外鍵,兩張表的關聯關係 --> 18 <key column="clazzid"></key> 19 20 <!-- 另外一端對應的類 --> 21 <one-to-many class="com.bjsxt.hibernate.pojo.AStudent"/> 22 </set> 23 </class> 24 </hibernate-mapping>
AStudent.hbm.xml -->多端
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 3 <!-- 4 hbm.xml的做用 5 將pojo中每個屬性對應到表的列名 6 --> 7 <hibernate-mapping> 8 <class name="com.bjsxt.hibernate.pojo.AStudent" table="a_student"> 9 <!-- id必須寫name --> 10 <id name="id"> 11 <generator class="native"/> 12 </id> 13 <property name="name"/> 14 <property name="createTime"/> 15 <many-to-one name="clazz" column="clazzid" class="com.bjsxt.hibernate.pojo.AClazz" cascade="save-update"></many-to-one> 16 </class> 17 </hibernate-mapping>
運用
瞭解了inverse,cascade 下面一對多(雙向)綜合運用一下
1.下面這張截圖時eclipse中的項目目錄
2.根據上面的目錄結構首先建立pojo類
2.1 AClazz.java -- 一端
1 package com.bjsxt.hibernate.pojo; 2 3 import java.util.Date; 4 import java.util.HashSet; 5 import java.util.Set; 6 7 8 public class AClazz { 9 private int id; 10 private String name; 11 private Date createTime; 12 //一個班級能夠放多個學生,一對多 13 private Set<AStudent> studentSet = new HashSet<>() ; 14 public AClazz() { 15 16 } 17 public AClazz(int id, String name, Date createTime) { 18 super(); 19 this.id = id; 20 this.name = name; 21 this.createTime = createTime; 22 } 23 public int getId() { 24 return id; 25 } 26 public void setId(int id) { 27 this.id = id; 28 } 29 public String getName() { 30 return name; 31 } 32 public void setName(String name) { 33 this.name = name; 34 } 35 public Date getCreateTime() { 36 return createTime; 37 } 38 public void setCreateTime(Date createTime) { 39 this.createTime = createTime; 40 } 41 @Override 42 public String toString() { 43 return "AClazz [id=" + id + ", name=" + name + ", createTime=" + createTime + "]"; 44 } 45 @Override 46 public int hashCode() { 47 final int prime = 31; 48 int result = 1; 49 result = prime * result + ((createTime == null) ? 0 : createTime.hashCode()); 50 result = prime * result + id; 51 result = prime * result + ((name == null) ? 0 : name.hashCode()); 52 return result; 53 } 54 @Override 55 public boolean equals(Object obj) { 56 if (this == obj) 57 return true; 58 if (obj == null) 59 return false; 60 if (getClass() != obj.getClass()) 61 return false; 62 AClazz other = (AClazz) obj; 63 if (createTime == null) { 64 if (other.createTime != null) 65 return false; 66 } else if (!createTime.equals(other.createTime)) 67 return false; 68 if (id != other.id) 69 return false; 70 if (name == null) { 71 if (other.name != null) 72 return false; 73 } else if (!name.equals(other.name)) 74 return false; 75 return true; 76 } 77 public Set<AStudent> getStudentSet() { 78 return studentSet; 79 } 80 public void setStudentSet(Set<AStudent> studentSet) { 81 this.studentSet = studentSet; 82 } 83 }
2.1 AStudent.java -- 多端
1 package com.bjsxt.hibernate.pojo; 2 3 import java.util.Date; 4 5 public class AStudent { 6 private int id; 7 private String name; 8 private Date createTime; 9 // 多對一,多個學生對應一個班級 10 private AClazz clazz; 11 12 public AStudent() { 13 } 14 15 public AStudent(int id, String name, Date createTime) { 16 super(); 17 this.id = id; 18 this.name = name; 19 this.createTime = createTime; 20 } 21 22 public int getId() { 23 return id; 24 } 25 26 public void setId(int id) { 27 this.id = id; 28 } 29 30 public String getName() { 31 return name; 32 } 33 34 public void setName(String name) { 35 this.name = name; 36 } 37 38 public Date getCreateTime() { 39 return createTime; 40 } 41 42 public void setCreateTime(Date createTime) { 43 this.createTime = createTime; 44 } 45 46 @Override 47 public String toString() { 48 return "AStudent [id=" + id + ", name=" + name + ", createTime=" + createTime + "]"; 49 } 50 51 @Override 52 public int hashCode() { 53 final int prime = 31; 54 int result = 1; 55 result = prime * result + ((createTime == null) ? 0 : createTime.hashCode()); 56 result = prime * result + id; 57 result = prime * result + ((name == null) ? 0 : name.hashCode()); 58 return result; 59 } 60 61 @Override 62 public boolean equals(Object obj) { 63 if (this == obj) 64 return true; 65 if (obj == null) 66 return false; 67 if (getClass() != obj.getClass()) 68 return false; 69 AStudent other = (AStudent) obj; 70 if (createTime == null) { 71 if (other.createTime != null) 72 return false; 73 } else if (!createTime.equals(other.createTime)) 74 return false; 75 if (id != other.id) 76 return false; 77 if (name == null) { 78 if (other.name != null) 79 return false; 80 } else if (!name.equals(other.name)) 81 return false; 82 return true; 83 } 84 85 public AClazz getClazz() { 86 return clazz; 87 } 88 89 public void setClazz(AClazz clazz) { 90 this.clazz = clazz; 91 } 92 }
3.配置映射文件
3.1 AClazz.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- hbm.xml的做用 將pojo中每個屬性對應到表的列名 --> <hibernate-mapping> <class name="com.bjsxt.hibernate.pojo.AClazz" table="a_clazz"> <!-- id必須寫name --> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="createTime"/> <!-- 一對多如何設置 --> <set name="studentSet" inverse="false" cascade="all"> <!-- 配置外鍵,兩張表的關聯關係 --> <key column="clazzid"></key> <!-- 另外一端對應的類 --> <one-to-many class="com.bjsxt.hibernate.pojo.AStudent"/> </set> </class> </hibernate-mapping>
以上的<set>....</set>它就至關於一個紐帶,比如一我的牽了多隻牛
3.2 AStudent.hbm.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <!-- hbm.xml的做用 將pojo中每個屬性對應到表的列名 --> <hibernate-mapping> <class name="com.bjsxt.hibernate.pojo.AStudent" table="a_student"> <!-- id必須寫name --> <id name="id"> <generator class="native"/> </id> <property name="name"/> <property name="createTime"/> <many-to-one name="clazz" column="clazzid" class="com.bjsxt.hibernate.pojo.AClazz" cascade="save-update"></many-to-one> </class> </hibernate-mapping>
別忘了在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"> <!-- session-factory == DriverManager session == Connection --> <hibernate-configuration> <session-factory> <!-- 驅動包名 --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <!-- JDBC URL --> <property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/hibernate</property> <!-- JDBC 用戶名 --> <property name="hibernate.connection.username">root</property> <!-- JDBC 密碼 --> <property name="hibernate.connection.password">admin</property> <!-- 官方語言,告訴hibernate鏈接的是哪一個數據庫 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 是否顯示SQL語句 --> <property name="hibernate.show_sql">true</property> <!-- 格式化SQL語句 --> <property name="hibernate.format_sql">true</property> <!-- create:每次執行,都建立一張新表,將原來的記錄刪除 update:每次執行,若是映射文件有修改,僅僅修改表結構 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 告訴hibernate核心配置文件,加載哪些映射文件 --> <mapping resource="com/bjsxt/hibernate/pojo/AStudent.hbm.xml"/> <mapping resource="com/bjsxt/hibernate/pojo/AClazz.hbm.xml"/> </session-factory> </hibernate-configuration>
注意測試一之後的全部測試,都是基於測試一上所作的修改
4.JUnit測試
保持 AClazz.hbm.xml 和 AStudent.hbm.xml 中級聯關係不變,只修改AClazz.hbm.xml 中的inverse屬性
建立一個班級,多個學生,由於配置了級聯關係,因此我把session.save(student)註釋了,這樣只須要保存班級便可
@Test public void inserClazz() { Session session = null; Transaction transaction = null; try { // 獲取Session==Connection session = sessionFactory.openSession(); // 手動開啓事務 transaction = session.beginTransaction(); // 建立班級 AClazz clazz = new AClazz(); clazz.setName("405"); clazz.setCreateTime(new Date()); Set<AStudent> studentSet = new HashSet<>(); // 一個班級有多個學生 for (int i = 0; i < 3; i++) { AStudent student = new AStudent(); student.setName("張三_" + i); student.setCreateTime(new Date()); studentSet.add(student); // session.save(student); } // 將學生集合放到班級中 clazz.setStudentSet(studentSet); session.save(clazz); // 手動提交事務 transaction.commit(); } catch (Exception e) { e.printStackTrace(); // 事務回滾 transaction.rollback(); } finally { if (session != null && session.isOpen()) { session.close(); } } }
如今保持 AClazz.hbm.xml 和 AStudent.hbm.xml 中級聯關係不變,只修改AClazz.hbm.xml 中的inverse屬性
測試一:當AClazz.hbm.xml 中inverse屬性爲false時
運行inserClazz() ,控制檯語句輸出結果以下:
Hibernate: create table a_clazz ( id integer not null auto_increment, name varchar(255), createTime datetime, primary key (id) ) 2016-11-10 18:16:20,238 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student 2016-11-10 18:16:20,239 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student Hibernate: create table a_student ( id integer not null auto_increment, name varchar(255), createTime datetime, clazzid integer, primary key (id) ) Hibernate: alter table a_student add constraint FK9jjuqt3yl34nmy329k44uje4j foreign key (clazzid) references a_clazz (id) 2016-11-10 18:16:20,291 INFO [main] fetch.FetchTest (FetchTest.java:48) - --init--sessionFactory:org.hibernate.internal.SessionFactoryImpl@3ff57625 Hibernate: insert into a_clazz (name, createTime) values (?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: update a_student set clazzid=? where id=? Hibernate: update a_student set clazzid=? where id=? Hibernate: update a_student set clazzid=? where id=?
mysql數據庫截圖以下:
a_clazz
a_student
解析:咱們能夠看到,先是建立了兩個表,教室表和學生表,由於我配置了一對多的關係one-to-many(我這裏是雙向配置,即一對多雙向配置,只配一端的也能夠),因此會爲a_stuent表設置外鍵,
由於我再AClazz.hbm.xml中配置了級聯all,因此我再保存班級的時候會自動將學生保存到數據庫中。
又由於我將AClazz.hbm.xml中的inverse屬性設置成了false,表明着教師表能夠維護它與學生表之間的關係,也就是在保存班級的時候,能夠將學生表中的外鍵clazzid設置上,因此最後也就出現了三條update語句,爲a_student的外鍵更新值,正是由於在一端將inverse設置爲false(inverse不設置的話,默認值false),因此再保存完學生以後,一端還要爲多端設置外鍵,如今是插入了三個學生,若是我要是插入了10000個學生呢?There is no doubt that 最後會多出10000條更新語句,因此說咱們通常將一端的inverse設置爲false,將維護關係的權利交給多方,而後配置級聯(一端爲all,多端爲save-update),這樣的話,咱們只須要保存多端的對象,即學生,那麼最後就不會生成update語句了。(測試四驗證這句話)
固然若是我把AClazz.hbm.xml中的inverse屬性設置成true,表明着教室表失去了維護關係的權利,也就是說在保存班級的時候,不能夠將學生表中的外鍵clazzid設置上,因此最後天然也就不會出現三條update語句,那麼a_student中外鍵那一列也就變成了null
下面驗證我剛纔的說法:
測試二:當AClazz.hbm.xml 中inverse屬性爲true時
運行inserClazz() ,控制檯語句輸出結果以下:
create table a_student ( id integer not null auto_increment, name varchar(255), createTime datetime, clazzid integer, primary key (id) ) Hibernate: alter table a_student add constraint FK9jjuqt3yl34nmy329k44uje4j foreign key (clazzid) references a_clazz (id) 2016-11-10 18:33:10,940 INFO [main] fetch.FetchTest (FetchTest.java:48) - --init--sessionFactory:org.hibernate.internal.SessionFactoryImpl@3ff57625 Hibernate: insert into a_clazz (name, createTime) values (?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?)
mysql數據庫截圖以下:
a_clazz
a_student
看見了吧,以上的結果證明了我剛纔的說法是正確的
測試三:如今將AClazz.hbm.xml 中的級聯關係刪除,而且中inverse屬性爲true,其餘保持不變
運行inserClazz() ,控制檯語句輸出結果以下:
Hibernate: create table a_clazz ( id integer not null auto_increment, name varchar(255), createTime datetime, primary key (id) ) 2016-11-10 18:37:12,045 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student 2016-11-10 18:37:12,046 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student Hibernate: create table a_student ( id integer not null auto_increment, name varchar(255), createTime datetime, clazzid integer, primary key (id) ) Hibernate: alter table a_student add constraint FK9jjuqt3yl34nmy329k44uje4j foreign key (clazzid) references a_clazz (id) 2016-11-10 18:37:12,107 INFO [main] fetch.FetchTest (FetchTest.java:48) - --init--sessionFactory:org.hibernate.internal.SessionFactoryImpl@187eb9a8 Hibernate: insert into a_clazz (name, createTime) values (?, ?) Hibernate: update a_student set clazzid=? where id=? 2016-11-10 18:37:12,552 ERROR [main] internal.ExceptionMapperStandardImpl (ExceptionMapperStandardImpl.java:39) - HHH000346: Error during managed flush [org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.bjsxt.hibernate.pojo.AStudent] 2016-11-10 18:37:12,565 INFO [main] internal.AbstractBatchImpl (AbstractBatchImpl.java:193) - HHH000010: On release of batch it still contained JDBC statements java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.bjsxt.hibernate.pojo.AStudent at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:144) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1411) at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:475) at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3168) at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2382) at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:220) at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68) at com.bjsxt.hibernate.fetch.FetchTest.inserClazz(FetchTest.java:86) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 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:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance beforeQuery flushing: com.bjsxt.hibernate.pojo.AStudent at org.hibernate.engine.internal.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:279) at org.hibernate.type.EntityType.getIdentifier(EntityType.java:462) at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:151) at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:894) at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1317) at org.hibernate.persister.collection.OneToManyPersister.recreate(OneToManyPersister.java:170) at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1405) ... 33 more
能夠看到hibernate表幫咱們建立了,外鍵也幫咱們加上了,可是卻報出了異常,
報的是一個臨時對象異常:對象的引用是一個爲保存的持久化實例,說白了就是沒有設置級聯
可是hibernate幫咱們把表建好了,只不過是空的
a_clazz
a_student
測試四:如今將AClazz.hbm.xml 中的級聯關係依舊設置爲all,而且inverse屬性爲true,
單元測試更改以下:(注意我把set集合放學生,以及最後的保存班級都註釋了,下面的測試代碼中只保存了學生 )
/** * 保存方法 */ @Test public void inserClazz() { Session session = null; Transaction transaction = null; try { // 獲取Session==Connection session = sessionFactory.openSession(); // 手動開啓事務 transaction = session.beginTransaction(); // 建立班級 AClazz clazz = new AClazz(); clazz.setName("405"); clazz.setCreateTime(new Date()); Set<AStudent> studentSet = new HashSet<>(); // 一個班級有多個學生 for (int i = 0; i < 3; i++) { AStudent student = new AStudent(); student.setName("張三_" + i); student.setCreateTime(new Date()); student.setClazz(clazz); //studentSet.add(student); session.save(student);//只保存學生,由於多端配置了級聯關係,save-update,因此保存學生的時候先保存班級 } /*// 將學生集合放到班級中 clazz.setStudentSet(studentSet); session.save(clazz);*/ // 手動提交事務 transaction.commit(); } catch (Exception e) { e.printStackTrace(); // 事務回滾 transaction.rollback(); } finally { if (session != null && session.isOpen()) { session.close(); } } }
運行inserClazz() ,控制檯語句輸出結果以下:
Hibernate: create table a_clazz ( id integer not null auto_increment, name varchar(255), createTime datetime, primary key (id) ) 2016-11-10 18:55:25,462 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student 2016-11-10 18:55:25,467 INFO [main] internal.InformationExtractorJdbcDatabaseMetaDataImpl (InformationExtractorJdbcDatabaseMetaDataImpl.java:393) - HHH000262: Table not found: a_student Hibernate: create table a_student ( id integer not null auto_increment, name varchar(255), createTime datetime, clazzid integer, primary key (id) ) Hibernate: alter table a_student add constraint FK9jjuqt3yl34nmy329k44uje4j foreign key (clazzid) references a_clazz (id) 2016-11-10 18:55:25,549 INFO [main] fetch.FetchTest (FetchTest.java:48) - --init--sessionFactory:org.hibernate.internal.SessionFactoryImpl@3ff57625 Hibernate: insert into a_clazz (name, createTime) values (?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?) Hibernate: insert into a_student (name, createTime, clazzid) values (?, ?, ?)
mysql數據庫截圖以下:
a_clazz
a_student
解析:從測試四的運行結果,咱們能夠清楚的看到,在保存學生的時候(多端),由於學生映射xml中配置了級聯,因此先保存班級(一端),這樣保存班級的時候返回該班級的主鍵,而後保存學生的時候就有了外鍵,因此最後並無三條更新語句,從而提升了性能
參考:http://www.cnblogs.com/o-andy-o/archive/2012/03/26/2418235.html
以上是我對hibernate中inverse和cascade屬性的理解,若有不對的地方,歡迎你們及時指正!!!
©All rights reserved