一对多(多对一)关系中的inverse和cascade属性

首先说一下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  --  一端

 AClazz.java

2.1 AStudent.java  --  多端

 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() ,控制台语句输出结果如下:

 

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() ,控制台语句输出结果如下:

 

mysql数据库截图如下:

a_clazz

a_student

  看见了吧,以上的结果证实了我刚才的说法是正确的

测试三:现在将AClazz.hbm.xml 中的级联关系删除,并且中inverse属性为true,其他保持不变

  运行inserClazz() ,控制台语句输出结果如下:

 

  可以看到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() ,控制台语句输出结果如下:

 

mysql数据库截图如下:

a_clazz

a_student

解析:从测试四的运行结果,我们可以清楚的看到,在保存学生的时候(多端),因为学生映射xml中配置了级联,所以先保存班级(一端),这样保存班级的时候返回该班级的主键,然后保存学生的时候就有了外键,所以最后并没有三条更新语句,从而提高了性能

参考:http://www.cnblogs.com/o-andy-o/archive/2012/03/26/2418235.html

时间: 2024-10-21 14:15:18

一对多(多对一)关系中的inverse和cascade属性的相关文章

SQLAlchemy_定义(一对一/一对多/多对多)关系

目录 Basic Relationship Patterns One To Many One To One Many To Many Basic Relationship Patterns 基本关系模式 The imports used for each of the following sections is as follows: 下列的 import 语句,应用到接下来所有的代章节中: from sqlalchemy import Table, Column, Integer, Forei

PD 15.1 安装 破解 , 简单使用 (一对多,多对多关系生成sql脚本)

  1:运行  PowerDesigner15_Evaluation.exe 默认   2: 安装完毕后,不要执行,下面我们进行 破解 把 PowerDesigner15汉化+注册补丁  下的所有文件,覆盖 PD的安装目录下的文件 然后我们打开 PD,点击 帮助 –> About  看到下面窗口中红色方框,就表示已经破解 + 汉化成功 PD 15.1 安装 破解 , 简单使用 (一对多,多对多关系生成sql脚本)

Entity Framework 6 Recipes 2nd Edition(10-9)译 -&gt; 在多对多关系中为插入和删除使用存储过程

10-9. 在多对多关系中为插入和删除使用存储过程 问题 想要在一个无载荷的多对多关系中使用存储过程(存储过程只影响关系的连接表) 解决方案 假设有一个多对多关系的作者( Author)表和书籍( Book)表. 用连接表AuthorBook来做多对多关系,如 Figure 10-11.所示: Figure 10-11. A payload-free, many-to-many relationship between an Author and a Book 当把表生成模型,那么模型就如Fig

Hibernate【inverse和cascade属性】知识要点

Inverse属性 Inverse属性:表示控制权是否转移.. true:控制权已转移[当前一方没有控制权] false:控制权没有转移[当前一方有控制权] Inverse属性,是在维护关联关系的时候起作用的.只能在"一"的一方中使用该属性!Inverse属性的默认值为fasle,也就是当前一方是有控制权的 从一下的几个方面看看Inverse在维护关联关系时是否起作用: 保存数据 获取数据 解除关联关系 删除数据对关联关系的影响 保存数据 将inverse属性设置为ture,使dept

闲聊Hibernate中的inverse和cascade

小小的记录一下自己对这两个属性的理解. 假如有两个类 class Group{ private int id; private Set<User> users  = new ... } class User{ private int id; private Group  group = new .. } 他们之间是 一对多的关系   Group (1) ---> User (N) inverse   属性的意思是  是否维护 他们之间的关系 ,换句通俗的话来说就是  是否  设置 外键

关联映射 ---- Hibernate之多对多关系

叙:上一章详细的记录了关联映射中的"一对多|多对一"关系.级联操作.关系的维护等知识点,本章节轻风学习记录的是级联关系中的"多对多"关系: Hibernate的"多对多"级联关系 1. 介绍 在生活中很多关系都是多对多的,比如一个人在公司是技术部的同时也是营销部的(只是个例子),所以,多对对关系是很普遍的,一个对象有多个角色,而一个角色又可以有多个对象,因此最正确的做法是在对象与角色之间创建一个新的表,用来保存对象和角色的主键,方便调用查看相应的

关于hibernate中多对多关系

关于多对多关系 数据库:在使用多对多的关系时,我们可以使用复合主键.也可以不使用,直接引入外键同样可以实现. 在数据库中使用多对多关系时,需要一个中间表. 多对多关系中的数据库结构如下: 表:Orders 字段:orderid(主键)..... 表:Users 字段:usersid(主键),.... 中间表: cy_order_user 字段:cy_order_user主键id 外键:cy_orderid(引入Orders表) 外键:cy_usersid(引入Users表) 注意:中间表的外键是

数据库的 一对多、多对一、一对一、多对多 关系

数据库实体间有三种对应关系:一对一,一对多,多对多.一对一关系示例:一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号.一对多关系示例:一个学生只属于一个班,但是一个学院有多名学生.多对多关系示例:一个学生可以选择多门课,一门课也有多名学生.这三种关系在数据库中逻辑结构处理分析:1.一对多关系处理:我们以学生和班级之间的关系来说明一对多的关系处理方法.假设现有基本表学生表(学号,姓名,--),班级表(班级号,备注信息,--). 方法一:新增一个关系表,用来表示学生与班级的属于关系,该关

表关系(一对一,一对多,多对多)

可以在数据库图表中的表之间创建关系,以显示一个表中的列与另一个表中的列是如何相链接的. 在一个关系型数据库中,利用关系可以避免多余的数据.例如,如果设计一个可以跟踪图书信息的数据库,您需要创建一个名为 titles 的表,它用来存储有关每本书的信息,例如书名.出版日期和出版社.您也可能保存有关出版社的信息,诸如出版社的电话.地址和邮政编码.如果您打算在 titles 表中保存所有这些信息,那么对于某出版社出版的每本书都会重复该出版社的电话号码. 更好的方法是将有关出版社的信息在单独的表,publ