Hibernate学习笔记(四)—— 表与表的关系

一、一对多|多对一

1.1 关系表达

1.1.1 表中的表达

  建表原则:在多的一方创建外键指向一的一方的主键。

  

1.1.2 实体中的表达

    

  【客户实体】

public class Customer {

    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_linkman;
    private String cust_phone;
    private String cust_mobile;
    
    // 一个客户有多个联系人:客户中应该放有联系人的集合
    private Set<LinkMan> linkMans = new HashSet<>();

    get/set...
}

  【联系人实体】

public class LinkMan {
    
     private Long lkm_id;
     private String lkm_name;
     private String lkm_gender;
     private String lkm_phone;
     private String lkm_mobile;
     private String lkm_email;
     private String lkm_qq;
     private String lkm_position;
     private String lkm_memo;

     // Hibernate是一个ORM框架:在关系型数据库中描述表与表之间的关系,使用的是外键。开发语言使用的是Java,面向对象的
     private Customer customer;
  
   get/set...
}

1.1.3 配置文件中的表达

  【客户的映射】

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.domain" >
    <class name="Customer" table="cst_customer" >
        <id name="cust_id"  >
            <generator class="native"></generator>
        </id>
        
        <property name="cust_name" column="cust_name" ></property>
        <property name="cust_source" column="cust_source" ></property>
        <property name="cust_industry" column="cust_industry" ></property>
        <property name="cust_level" column="cust_level" ></property>
        <property name="cust_linkman" column="cust_linkman" ></property>
        <property name="cust_phone" column="cust_phone" ></property>
        <property name="cust_mobile" column="cust_mobile" ></property>
        
        <!-- 配置关联对象 -->
        <!-- name属性:多的一方集合的属性名称 -->
        <set name="linkMans">
            <!-- column属性:多的一方外键的名称 -->
            <key column="lkm_cust_id"></key>
            <!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
            <one-to-many class="LinkMan"/>
        </set>
    </class>
</hibernate-mapping>

  【联系人的映射】

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.domain" >
    <class name="LinkMan" table="cst_linkman" >
        <id name="lkm_id"  >
            <generator class="native"></generator>
        </id>
        
        <property name="lkm_name" column="lkm_name" ></property>
        <property name="lkm_gender" column="lkm_gender" ></property>
        <property name="lkm_phone" column="lkm_phone" ></property>
        <property name="lkm_mobile" column="lkm_mobile" ></property>
        <property name="lkm_email" column="lkm_email" ></property>
        <property name="lkm_qq" column="lkm_qq" ></property>
        <property name="lkm_position" column="lkm_position" ></property>
        <property name="lkm_memo" column="lkm_memo" ></property>
        
        <!-- 配置关联对象 -->
        <!-- name:一的一方的对象的名称
            class:一的一方类的全路径
            column:表中外键的名称
         -->
        <many-to-one name="customer" class="Customer" column="lkm_cust_id"/>
    </class>
</hibernate-mapping>

1.2 测试代码

  • 保存
    // 保存一个客户和两个联系人
        @Test
        public void testSave() throws Exception {
            // 获得session
            Session session = HibernateUtils.openSession();
            // 开启事务
            Transaction tx = session.beginTransaction();
            // 创建一个客户
            Customer customer = new Customer();
            customer.setCust_name("李总");
            
            // 创建两个联系人
            LinkMan linkMan1 = new LinkMan();
            linkMan1.setLkm_name("张秘书");
            LinkMan linkMan2 = new LinkMan();
            linkMan2.setLkm_name("王助理");
            
            // 表达一对多,客户下有多个联系人
            customer.getLinkMans().add(linkMan1);
            customer.getLinkMans().add(linkMan2);
            
            // 表达多对一,联系人属于哪个客户
            linkMan1.setCustomer(customer);
            linkMan2.setCustomer(customer);
            
            session.save(customer);
            session.save(linkMan1);
            session.save(linkMan2);
            
            // 提交事务
            tx.commit();
            // 关闭资源
            session.close();
        }
  • 增加
    // 为客户增加联系人
        @Test
        public void testAdd() throws Exception {
            // 获得session
            Session session = HibernateUtils.openSession();
            // 开启事务
            Transaction tx = session.beginTransaction();
            
            // 获得要操作的客户对象
            Customer customer = session.get(Customer.class, 1l);
            // 创建联系人
            LinkMan linkMan = new LinkMan();
            linkMan.setLkm_name("赵秘书");
            // 将联系人添加到客户中
            customer.getLinkMans().add(linkMan);
            // 将客户设置到联系人中
            linkMan.setCustomer(customer);
            // 执行保存
            session.save(linkMan);
            
            tx.commit();
            session.close();
        }
  • 删除
    // 为客户删除联系人
        @Test
        public void testDelete() throws Exception {
            // 获得session
            Session session = HibernateUtils.openSession();
            // 开启事务
            Transaction tx = session.beginTransaction();
            
            // 获得要操作的客户对象
            Customer customer = session.get(Customer.class, 1l);
            // 获得要移除的联系人
            LinkMan linkMan = session.get(LinkMan.class, 2l);
            // 将联系人从客户集合中移除
            customer.getLinkMans().remove(linkMan);
            linkMan.setCustomer(null);
            
            tx.commit();
            session.close();
        }

1.3 进阶操作

  级联操作是指当主控方执行保存、更新或者删除操作时,其关联的对象(被控方)也执行相同的操作。在映射文件中通过对cascade属性的设置来控制是否对关联对象采用级联操作,级联操作对各种关联关系都是有效的。

1.3.1 级联保存或更新

  级联是有方向性的,所谓的方向性指的是,在保存一的一方级联多的一方,和在保存多的一方级联一的一方。

【保存客户级联联系人】

  首先要确定我们要保存的主控方是哪一方,我们要保存客户,所以客户是主控方,那么需要在客户的映射文件中(Customer.hbm.xml)进行如下配置:

<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="save-update">
    <!-- column属性:多的一方外键的名称 -->
    <key column="lkm_cust_id"></key>
    <!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
    <one-to-many class="LinkMan"/>
</set>

   编写测试代码:

// 级联保存:只保存一边的问题
    // 级联是有方向性的,保存客户同时级联客户的联系人
    // 在Customer.hbm.xml的<set>标签上配置cascade="save-update"
    @Test
    public void testCascade() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        // 创建一个客户
        Customer customer = new Customer();
        customer.setCust_name("刘总");
        
        // 创建联系人
        LinkMan linkMan = new LinkMan();
        linkMan.setLkm_name("王秘书");
        
        // 建立关系
        customer.getLinkMans().add(linkMan);
        linkMan.setCustomer(customer);
        
        session.save(customer);
        
        tx.commit();
        session.close();
    }

 【保存联系人级联客户】

   同样我们需要确定主控方,现在我们的主控方是联系人。所以需要在联系人的映射文件中(LinkMan.hbm.xml)进行配置:

<!-- 配置关联对象 -->
        <!-- name:一的一方的对象的名称
            class:一的一方类的全路径
            column:表中外键的名称
         -->
        <many-to-one name="customer" cascade="save-update" class="Customer" column="lkm_cust_id"/>

   测试代码:

// 级联保存:只保存一边的问题
    // 级联是有方向性的,保存联系人同时级联客户
    // 在LinkMan.hbm.xml的<many-to-one>标签上配置cascade="save-update"
    @Test
    public void testCascade2() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        // 创建客户
        Customer customer = new Customer();
        customer.setCust_name("张总");
        
        // 创建联系人
        LinkMan linkMan = new LinkMan();
        linkMan.setLkm_name("柳助理");
        
        // 建立关系
        customer.getLinkMans().add(linkMan);
        linkMan.setCustomer(customer);
        
        session.save(linkMan);
        
        tx.commit();
        session.close();
    }

   到这我们已经可以看到级联保存或更新的效果了。那么我们维护的时候都是双向的关系维护。这种关系到底表述的是什么含义呢?我们可以通过下面的测试来更进一步了解关系的维护(对象导航)的含义。

1.3.2 测试对象导航的问题

  我们所说的对象导航其实就是在维护双方的关系。

customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);

  这种关系有什么用途呢?我们可以通过下面的测试来更进一步学习Hibernate。

  我们在客户和联系人端都配置了cascade="save-update",然后进行如下的关系设置。会产生什么样的效果呢?

 1 // 测试对象导航和级联操作
 2     @Test
 3     public void testCascade3() throws Exception {
 4         Session session = HibernateUtils.openSession();
 5         Transaction tx = session.beginTransaction();
 6 
 7         // 创建一个客户
 8         Customer customer = new Customer();
 9         customer.setCust_name("赵总");
10 
11         // 创建三个联系人
12         LinkMan linkMan1 = new LinkMan();
13         linkMan1.setLkm_name("李秘书");
14         LinkMan linkMan2 = new LinkMan();
15         linkMan2.setLkm_name("王秘书");
16         LinkMan linkMan3 = new LinkMan();
17         linkMan3.setLkm_name("张助理");
18 
19         // 建立关系
20         linkMan1.setCustomer(customer);
21         customer.getLinkMans().add(linkMan2);
22         customer.getLinkMans().add(linkMan3);
23 
24         // 条件是双方都设置了cascade="save-update"
25         session.save(linkMan1); // 数据库中有几条记录?发送了几条insert语句? 4条
26         // session.save(customer); // 发送了几条insert语句? 3条
27         // session.save(linkMan2); // 发送了几条insert语句? 1条
28         
29         tx.commit();
30         session.close();
31     }

  我们在执行第25行代码时,问会执行几条insert语句?其实发现会有4条insert语句,因为联系人1关联了客户,客户又关联了联系人2和联系人3,所以当保存联系人1的时候,联系人1是可以进入到数据库的,它关联的客户也是会进入到数据库的(因为联系人一端配置了级联),那么客户进入到数据库以后,联系人2和联系人3也同样会进入到数据库(因为客户一端配置了级联),所以会有4条insert语句。

  我们在执行26行的时候,问会有几条insert语句?这时我们保存的客户对象关联了联系人2和联系人3,那么客户在进入数据库的时候,联系人2和联系人3也会进入到数据库,所以是3条insert语句。

  同理我们执行27行代码的时候,只会执行1条insert语句。因为联系人2会保存到数据库,但是联系人2没有对客户建立关系,所以客户不会保存到数据库。

 1.3.3 Hibernate的级联删除

  我们之前学习过级联保存或更新,那么再来看级联删除也就不难理解了,级联删除也是有方向性的,删除客户同时级联删除联系人,也可以删除联系人同时级联删除客户(这种需求很少)。

  原来JDBC中删除客户和联系人的时候,如果有外键的关系是不可以删除的,但是现在我们使用了Hibernate,其实Hibernate可以实现这样的功能,但是不会删除客户的同时删除联系人,默认情况下Hibernate会怎么做呢?我们来看下面的测试:

// 删除有级联关系的对象:(默认情况:先将关联对象的外键置为null,再删除)
    @Test
    public void demo5() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        Customer customer = session.get(Customer.class, 1l);
        session.delete(customer);
        
        tx.commit();
        session.close();
    }

  默认情况下如果客户下面还有联系人,Hibernate会将联系人的外键置为null,然后区删除客户。那么其实有的时候我们需要删除客户的时候,同时将客户关联的联系人一起删除。这个时候我们就需要使用Hibernate的级联删除操作了。

【删除客户的同时删除客户的联系人】

  确定删除的主控方是客户,所以需要在客户端(Customer.hbm.xml)配置:

<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="delete">
    <!-- column属性:多的一方外键的名称 -->
    <key column="lkm_cust_id"></key>
    <!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
    <one-to-many class="LinkMan"/>
</set>

  如果还想有之前的级联保存或更新,同时还想有级联删除,那么我们可以进行如下的配置:

<!-- 配置关联对象 -->
<!-- name属性:多的一方集合的属性名称 -->
<set name="linkMans" cascade="delete,save-update">
    <!-- column属性:多的一方外键的名称 -->
    <key column="lkm_cust_id"></key>
    <!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
    <one-to-many class="LinkMan"/>
</set>

  测试代码:

  // 级联删除:级联删除有方向性
    // 删除客户的同时删除联系人
    // 在Customer.hbm.xml的<set>标签上配置cascade="delete"
    @Test
    public void demo6() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        // 级联删除:必须是先查询再删除
        // 因为查询到客户,这个时候客户的联系人集合就会有数据
        Customer customer = session.get(Customer.class, 7l);
        session.delete(customer);
        
        tx.commit();
        session.close();
    }

【删除联系人的同时删除客户】

  这时候我们删除的是联系人,那么联系人就是主控方,需要在联系人端(LinkMan.hbm.xml)配置:

<!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
    class:一的一方类的全路径
    column:表中外键的名称
 -->
<many-to-one name="customer" cascade="delete" class="Customer" column="lkm_cust_id"/>

  如果既要保存或更新有级联操作,又要有级联删除的功能,也可以如下配置:

<!-- 配置关联对象 -->
<!-- name:一的一方的对象的名称
    class:一的一方类的全路径
    column:表中外键的名称
 -->
<many-to-one name="customer" cascade="delete,save-update" class="Customer" column="lkm_cust_id"/>

  测试代码:

  // 级联删除:级联删除有方向性
    // 删除联系人的同时删除客户
    // 在LinkMan.hbm.xml中的<many-to-one>标签上配置cascade="delete"
    @Test
    public void demo7() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        // 级联删除:必须是先查询再删除
        LinkMan linkMan = session.get(LinkMan.class, 4l);
        session.delete(linkMan);
        
        tx.commit();
        session.close();
    }

1.3.4 双向关联产生多余的SQL语句

【客户表】

  

【联系人表】

  

  有时候我们需要进行如下操作:将2号联系人关联给2号客户,也就是将2号李秘书这个联系人关联给2号刘总这个客户。

  编写修改2号客户关联的联系人的代码:

  // 将2号联系人关联的客户改为2号客户
    @Test
    public void demo8() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        Customer customer = session.get(Customer.class, 2l);
        LinkMan linkMan = session.get(LinkMan.class, 2l);
        
        linkMan.setCustomer(customer);
        customer.getLinkMans().add(linkMan);
        
        tx.commit();
        session.close();
    }

   运行上述代码,控制台输出如下内容:

    

  我们发现执行了两次update语句,其实这两个update都是修改了外键的操作,那么为什么发送两次呢?我们来分析一下产生这个问题的原因:

  

  我们已经分析过了,因为双向维护了关系,而且持久态对象可以自动更新数据库,更新客户的时候会修改一次外键,更新联系人的时候同样会修改一次外键。这样就会产生多余的SQL。问题产生了,我们该如何解决呢?

  其实解决的办法很简单,只需要一方放弃外键维护权即可。也就是说关系不是双方维护的,只需要交给某一方去维护就可以了。通常我们都是交给多的一方去维护。为什么呢?因为多的一方才是维护关系最好的地方。举个例子,一个老师对应多个学生,一个学生对应一个老师,这时典型的一对多。一个老师如果要记住所有学生的名字很难,但如果让每个学生记住老师的名字应该不难。其实就是这个道理。所有在一对多中,一的一方都会放弃外键的维护权(关系的维护);

  这个时候如果想让一的一方放弃外键的维护权,只需要进行如下配置即可:

<!-- 配置关联对象 -->
        <!-- name属性:多的一方集合的属性名称 -->
        <set name="linkMans" cascade="save-update" inverse="true">
            <!-- column属性:多的一方外键的名称 -->
            <key column="lkm_cust_id"></key>
            <!-- 多的一方类的全路径,如果前面的package中设置了包名,这里填类名即可 -->
            <one-to-many class="LinkMan"/>
        </set>

  inverse的默认值是false,代表不放弃外键的维护权,配置值为true,代表放弃了外键的维护权。这个时候再来执行之前的操作:

    

  这时候就不会出现上述问题了,不会产生多余的SQL了(因为一的一方已经放弃了外键的维护权)。

1.3.5 区分cascade和inverse

  // cascade和inverse
    // cascade强调的是操作一个对象的时候,是否操作其关联的对象
    // inverse强调的是外键的维护权
    @Test
    public void demo9() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        Customer customer = new Customer();
        customer.setCust_name("王总");
        
        LinkMan linkMan = new LinkMan();
        linkMan.setLkm_name("赵秘书");
        
        // 在Customer.hbm.xml的<set>标签上配置cascade="save-update" inverse="true"
        customer.getLinkMans().add(linkMan);
        
        session.save(customer);
        
        tx.commit();
        session.close();
    }

   这时候我们会发现,如果在<set>标签上配置cascade="save-update" inverse="true",那么执行保存客户的操作时,会发现客户和联系人都进入到数据库了,但是没有外键。是因为配置了cascade="save-update"所以客户关联的联系人会进入到数据库中,但是客户一端放弃了外键的维护权(inverse="true"),所以联系人插入到数据库以后是没有外键的。

二、多对多

2.1 关系表达

2.1.1 表中的表达

  

  

2.1.2 对象中的表达

  

【用户实体】

public class User {
    private Long user_id;
    private String user_code;
    private String user_name;
    private String user_password;
    private String user_state;
    
    // 用户所属的角色集合
    private Set<Role> roles = new HashSet<>();

  get/set... }

【角色实体】

public class Role {
    private Long role_id;
    private String role_name;
    private String role_memo;

    // 一个角色包含多个用户
    private Set<User> users = new HashSet<>();

  get/set... }

 2.1.3 配置文件中的表达

【用户的映射】User.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.domain" >
    <class name="User" table="sys_user" >
        <id name="user_id"  >
            <generator class="native"></generator>
        </id>
        
        <property name="user_code"/>
        <property name="user_name"/>
        <property name="user_password"/>
        <property name="user_state"/>
        
        <!-- 多对多关系表达 -->
        <!-- name:集合属性名
            table:配置中间表名
         -->
        <set name="roles" table="sys_user_role">
            <!-- column:外键,别人引用"我"的外键列名 -->
            <key column="user_id"></key>
            <!-- class:我与哪个类是多对多关系
                column:外键.我引用别人的外键列名
             -->
            <many-to-many class="Role" column="role_id"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

【角色的映射】Role.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.domain" >
    <class name="Role" table="sys_role" >
        <id name="role_id"  >
            <generator class="native"></generator>
        </id>
        
        <property name="role_name"/>
        <property name="role_memo"/>
        
        <!-- 多对多关系表达 -->
        <!-- name:集合属性名
            table:配置中间表名
         -->
        <set name="users" table="sys_user_role">
            <!-- column:外键,别人引用"我"的外键列名 -->
            <key column="role_id"></key>
            <!-- class:我与哪个类是多对多关系
                column:外键.我引用别人的外键列名
             -->
            <many-to-many class="User" column="user_id"></many-to-many>
        </set>
    </class>
</hibernate-mapping>

  在hibernate.cfg.xml中加入映射文件

<mapping resource="cn/itcast/domain/User.hbm.xml" />
<mapping resource="cn/itcast/domain/Role.hbm.xml" />

2.2 测试方法

  •  保存
      // 保存用户及角色
        @Test
        public void fun1() throws Exception {
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
            
            // 创建两个User
            User user1 = new User();
            user1.setUser_name("张三");
            
            User user2 = new User();
            user2.setUser_name("李四");
            
            // 创建两个Role
            Role role1 = new Role();
            role1.setRole_name("保洁");
            
            Role role2 = new Role();
            role2.setRole_name("保安");
            
            // 用户表达关系
            user1.getRoles().add(role1);
            user1.getRoles().add(role2);
            
            user2.getRoles().add(role2);
            
            // 角色表达关系
            role1.getUsers().add(user1);
            role2.getUsers().add(user1);
            role2.getUsers().add(user2);
            
            // 调用save方法一次保存
            session.save(user1);
            session.save(user2);
            session.save(role1);
            session.save(role2);
            
            tx.commit();
            session.close();
        }

     执行上述代码时,我们发现报错了:

  

【错误原因分析】

   映射文件中有一个inverse属性,不配置时默认为false,即要维护关系。这里两方关系都设置了{user1.getRoles().add(role1);role1.getUsers().add(user1);},从配置上讲,这两方都没放弃维护。多对多维护关系的手段是向中间表(sys_user_role)插入记录,如果要表达1号用户的角色是1号,那么中间表就会插入1-1记录。现在的情况是两方都默认维护关系,role在维护关系的时候,表达1-1,会在中间表插入1-1记录;user在维护关系的时候,也要往中间表插入数据,这时候user又要插入1-1记录。中间表往往是这两个id共同作为主键的,这两个1-1就会出现主键重复。

  之前的一对多两方都维护关系,却没发生异常,是因为它们维护关系,就是修改了外键字段,不存在向中间表插入记录,所以修改两次是没有问题的,至少不会报错,虽然效率会低点。而多对多维护关系是向中间表插入记录,两方都维护的时候,就会插入两条一样的记录,就会产生主键重复。

  

【解决方案】

  方法一:映射文件不改,从代码上来讲,只要一方不表达关系即可,这样在保存的时候,因为代码上并没有表达关系的逻辑,就不会给你操作了。这里我们可以让role的一方不表达关系,在代码上把相关代码注释,此时方法就可执行成功

    

   方法二:代码不改,在一方的映射文件中修改inverse为true,让它放弃维护关系。这里修改Role.hbm.xml

    

【结论】

   在多对多的保存操作中,如果进行了双向维护关系,就必须有一方放弃外键维护权。一般由被动方放弃,用户主动选择角色,角色是被选择的,所以一般角色要放弃外键维护权。但如果只进行单向维护关系,那么就不需要放弃外键维护权了。

2.3 进阶操作

2.3.1 级联保存或更新

【保存用户级联角色】

  保存的主控方是用户,需要在用户一端(User.hbm,xml)配置

  

  测试代码:

// 保存用户级联角色
    @Test
    public void fun2() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        // 创建两个User
        User user1 = new User();
        user1.setUser_name("刘备");

        User user2 = new User();
        user2.setUser_name("关羽");
        
        // 创建三个角色
        Role role1 = new Role();
        role1.setRole_name("人事管理");
        Role role2 = new Role();
        role2.setRole_name("行政管理");
        Role role3 = new Role();
        role3.setRole_name("财务管理");
        
        // 建立关系:如果建立了双向的关系,一定要有一方放弃外键维护权
        user1.getRoles().add(role1);
        user1.getRoles().add(role2);
        
        user2.getRoles().add(role3);
        user2.getRoles().add(role2);
        
        role1.getUsers().add(user1);
        role2.getUsers().add(user1);
        role2.getUsers().add(user2);
        role3.getUsers().add(user2);
        
        session.save(user1);
        session.save(user2);
        /*session.save(role1);
        session.save(role2);
        session.save(role3);*/
        
        tx.commit();
    }

【保存角色级联用户】

  保存的主控方是角色,需要在角色一端配置:

  

  测试代码:

// 保存角色级联用户
    @Test
    public void fun2() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        // 创建两个User
        User user1 = new User();
        user1.setUser_name("刘备");

        User user2 = new User();
        user2.setUser_name("关羽");
        
        // 创建三个角色
        Role role1 = new Role();
        role1.setRole_name("人事管理");
        Role role2 = new Role();
        role2.setRole_name("行政管理");
        Role role3 = new Role();
        role3.setRole_name("财务管理");
        
        // 建立关系:如果建立了双向的关系,一定要有一方放弃外键维护权
        user1.getRoles().add(role1);
        user1.getRoles().add(role2);
        
        user2.getRoles().add(role3);
        user2.getRoles().add(role2);
        
        role1.getUsers().add(user1);
        role2.getUsers().add(user1);
        role2.getUsers().add(user2);
        role3.getUsers().add(user2);
        
        /*session.save(user1);
        session.save(user2);*/
        session.save(role1);
        session.save(role2);
        session.save(role3);
        
        tx.commit();
    }

2.3.2 级联删除(了解)

  在多对多中级联删除是不会使用的,因为我们不会有这类的需求,比如删除用户,将用户关联的角色一起删除,或者删除角色的时候将用户删除掉,这时不合理的。但是级联删除的功能Hibernate已经提供了此功能,所以我们只需要了解即可。

【删除用户级联角色】

   主控方是用户,所以需要在用户端配置

  

  测试代码:

// 级联删除:删除用户,级联删除角色
    @Test
    public void fun3() throws Exception {
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        
        User user = session.get(User.class, 1l);
        session.delete(user);
        
        tx.commit();
        session.close();
    }

2.3.3 多对多的其他操作

  • 删除某个用户的角色
    // 将1号用户的1号角色去掉
        @Test
        public void fun4() throws Exception {
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
            
            // 查询1号用户
            User user = session.get(User.class, 1l);
            // 查询1号角色
            Role role = session.get(Role.class, 1l);
            
            // 操作集合 相当于操作数据库
            user.getRoles().remove(role);
            
            tx.commit();
        }
  • 将某个用户的角色改选
    // 将1号用户的2号角色去掉,改为3号角色
        @Test
        public void fun5() throws Exception {
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
            
            // 查询1号用户
            User user = session.get(User.class, 1l);
            // 查询2号角色
            Role role1 = session.get(Role.class, 2l);
            // 查询3号角色
            Role role2 = session.get(Role.class, 3l);
            
            user.getRoles().remove(role1);
            user.getRoles().add(role2);
            
            tx.commit();
        }
  • 给某个用户添加新角色
    // 给1号用户添加2号角色
        @Test
        public void fun6() throws Exception {
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
            
            // 查询1号用户
            User user = session.get(User.class, 1l);
            // 查询2号角色
            Role role = session.get(Role.class, 2l);
            
            user.getRoles().add(role);
            
            tx.commit();
        }

猜你喜欢

转载自www.cnblogs.com/yft-javaNotes/p/10252472.html