zoukankan      html  css  js  c++  java
  • 一对多(多对一)关系中的inverse和cascade属性

    转载请标明出处 http://www.cnblogs.com/haozhengfei/p/6049276.html 


    首先说一下inverse:

    "inverse" 直译过来就是"反转,使颠倒"的意思,书面化的解释为"是否将关系维护的权力交给对方"

    1.  在hibernate中inverse默认是false,也就是己方拥有维护关系的权利, 当然为true的话,就是把维护关系的权利交给了对方

    2.  在一对多的关系(多对一)中,通常将一端的inverse设置为false(一端设为true的话会多出更新语句,有性能问题,下面会讲到),而多对多的关系中,inverse的值只能有一个为true,因为如果双发都为true,那么双方都去维护关系,会造成中间关系表中出现重复的数据(这一点以后有深刻的理解,会补上解释的额)

    说到这里,那么问题来了,什么是关系?关系的具体体现又是什么?

    什么是关系?

      "关系"就是两个表之间的关系,通常为"一对多","一对一","多对多"三种关系,

    关系的具体体现是什么?

    暂且先看下面这张图,后面会详细讲解

    这是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

  • 相关阅读:
    数据库性能优化
    AutoDetectChangesEnabled及AddRange解决EF插入的性能问题
    实体框架 5 性能注意事项
    使用JS传递数组型数据回服务器
    Code First配合Entity Framework Power Tools Beta 4使用
    HighChart 体验之旅 (后台传递JSON参数和数据的方法)
    System.Transactions事务超时设置
    ASP.NET站点部署相关
    js 字符串转化成数字
    发布.net 4.0的站点到IIS7.5下时无法访问
  • 原文地址:https://www.cnblogs.com/haozhengfei/p/6049276.html
Copyright © 2011-2022 走看看