一對多(多對一)關係中的inverse和cascade屬性

轉載請標明出處 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 }
AClazz.java

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 }
AStudent.java

 

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=?
View Code

 

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
        (?, ?, ?)
View Code

 

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
View Code

 

   能夠看到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
        (?, ?, ?)
View Code

 

 

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

相關文章
相關標籤/搜索