• Hibernate第三天——表间关系与级联操作


    第三天,我们来使用Hibernate进行表之间一对多 多对多关系的操作:

      这里我们先利用两个例子进行表关系的回顾:

    一对多(重点): 例如分类和商品的关系,一个分类多个商品,一个商品属于一个分类
            CRM 客户关系管理
            客户和联系人:
            客户:一般指的是有业务往来的公司(例如百度、腾讯)
            联系人:公司里的员工(和联系人联系就联系上公司)

            这里的客户和联系人就是一对多的关系(一个公司多个员工,一个员工从属与一个公司)

      如何建表:通过外键建立关系
            在多的那一方建立一个外键(这个外键指向客户的主键)(因为在一的那一方无法创建出合理的外键)

          利用Hibernate建表无需我们手动建表,只需在 Hibernate中配置这个映射关系即可自动生成这个一对多的表

    多对多(重点):例如订单和商品的关系,一个订单有多个商品,一个商品可以属于多个订单
      用户和角色的关系:
      一个用户可以是多个角色,一个角色可以对应多个用户
      例如,小王是总经理,也可以是司机;小马可以是司机,也可以是秘书
      如何建表:
        需要建立第三张表维护两者的关系
        至少要有两个外键指向两张表的主键

    一对一(较少见):一夫一妻制(当然我们说的是中国的合法关系)

      下面我们进行一对多的配置

      一对多的配置:
        环境搭建完成后
        一、创建实体类(客户和联系人)
        二、让实体类之间先互相进行表示,客户实体类有多个联系人,一个联系人属于一个客户
          //一个客户多个联系人,要求使用集合表示,但必须使用Set(不可重复)
        三、配置映射关系
          1.一般一个实体类对应一个映射文件,配置完各自的基本配置
          2.配置一对多关系
          在客户的映射文件中表示所有的联系人
          在联系人表示联系人所属的客户
        四、创建核心配置文件
          引入映射配置文件(注意是两个)

      先看大致的一对多的目录结构:

        

    我们来看看实体类及其配置文件:

      客户:

    package cn.entity;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Customer {
    
        //客户ID
        private Integer cid;
        //客户名称
        private String custName;
        //客户级别
        private String custLevel;
        //客户来源
        private String custSource;
        //客户电话
        private String custPhone;
        //客户手机
        private String custMobile;
        
        //一个客户多个联系人,要求使用集合表示,但必须使用Set
        private Set<LinkMan> set_LinkMan = new HashSet<LinkMan>();
        
        
        public Set<LinkMan> getSet_LinkMan() {
            return set_LinkMan;
        }
        public void setSet_LinkMan(Set<LinkMan> set_LinkMan) {
            this.set_LinkMan = set_LinkMan;
        }
        //空构造器
        public Customer() {
            
        }
        public Integer getCid() {
            return cid;
        }
        public void setCid(Integer cid) {
            this.cid = cid;
        }
        public String getCustName() {
            return custName;
        }
        public void setCustName(String custName) {
            this.custName = custName;
        }
        public String getCustLevel() {
            return custLevel;
        }
        public void setCustLevel(String custLevel) {
            this.custLevel = custLevel;
        }
        public String getCustSource() {
            return custSource;
        }
        public void setCustSource(String custSource) {
            this.custSource = custSource;
        }
        public String getCustPhone() {
            return custPhone;
        }
        public void setCustPhone(String custPhone) {
            this.custPhone = custPhone;
        }
        public String getCustMobile() {
            return custMobile;
        }
        public void setCustMobile(String custMobile) {
            this.custMobile = custMobile;
        }
        
        
    }

      联系人:

    package cn.entity;
    
    public class LinkMan {
    
        //联系人编号
        private Integer lkm_id;
        //联系人姓名
        private String lkm_name;
        //联系人性别
        private String lkm_gender;
        //联系人电话
        private String lkm_phone;
        //一个联系人对应一个客户
        private Customer customer;
        
        public Customer getCustomer() {
            return customer;
        }
        public void setCustomer(Customer customer) {
            this.customer = customer;
        }
        //空构造器
        public LinkMan() {
            
        }
        public Integer getLkm_id() {
            return lkm_id;
        }
        public void setLkm_id(Integer lkm_id) {
            this.lkm_id = lkm_id;
        }
        public String getLkm_name() {
            return lkm_name;
        }
        public void setLkm_name(String lkm_name) {
            this.lkm_name = lkm_name;
        }
        public String getLkm_gender() {
            return lkm_gender;
        }
        public void setLkm_gender(String lkm_gender) {
            this.lkm_gender = lkm_gender;
        }
        public String getLkm_phone() {
            return lkm_phone;
        }
        public void setLkm_phone(String lkm_phone) {
            this.lkm_phone = lkm_phone;
        }
        
        
    }

      客户映射文件:

    <?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>
        <!-- 映射的类 name是类的全路径 table是表名 -->
        <class name="cn.entity.Customer" table="t_customer">
            <id name="cid" column="cid">
                <!-- 设置id增长策略 -->
                <generator class="native"></generator>
            </id>
            <!-- 配置其它属性 -->
            <property name="custName" column="custName"></property>
            <property name="custLevel" column="custLevel"></property>
            <property name="custSource" column="custSource"></property>
            <property name="custPhone" column="custPhone"></property>
            <property name="custMobile" column="custMobile"></property>
            
            <!-- 在客户的映射文件中表示所有的联系人 -->
            <!-- 使用set集合表示所有联系人,name为客户实体类中表示联系人的set集合名称 -->
            <set name="set_LinkMan" cascade="save-update,delete" inverse="true">
                <!-- column属性值为外键的名字 -->
                <key column="clid"></key>
                <!-- 多的那一方的全路径 -->
                <one-to-many class="cn.entity.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>
        <!-- 映射的类 name是类的全路径 table是表名 -->
        <class name="cn.entity.LinkMan" table="t_linkman">
            <id name="lkm_id" column="lkm_id">
                <!-- 设置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>
            <!-- 在联系人表示联系人所属的客户 -->
            <!-- name为表示客户的那个对象名称(不是对象类型) class为客户实体类的全路径 column为外键名称 -->
            <many-to-one name="customer" class="cn.entity.Customer" column="clid"></many-to-one>
        </class>
    </hibernate-mapping>

    在核心配置文件中引入资源(这里只写出核心的两句,其它相同的此处不再赘述):

            <!-- 三、引入映射文件 -->
            <mapping resource="cn/entity/Customer.hbm.xml"/>
            <mapping resource="cn/entity/LinkMan.hbm.xml"/>

      一对多级联操作 级联:cascade
        级联保存:
          添加了一个客户,为这个客户又添加了多个联系人,需要操作两张表
        级联删除:
          删除了一个客户,客户里的所有联系人也要删除
          直接删除时无法直接删除,它有外键的关联(需要先删完联系人,或把联系人的外键设置为Null)

        Hibernate进行了封装,可以方便的进行级联操作
        级联保存两种写法(一种麻烦,一种简化)
          第一种(底层写法)

        //一对多的级联保存
        @Test
        public void testAdd(){
            //请将声明放在try之外
            SessionFactory sessionFactory = null;
            Session session = null;
            Transaction tx = null;
            try{
                //得到工厂
                sessionFactory = HibernateUtils.getSessionFactory();
                //得到session
                session = sessionFactory.openSession();
                //开启事务
                tx = session.beginTransaction();
                
                //级联保存操作(较麻烦的方法)
                /*创建客户对象*/
                Customer cust = new Customer();
                cust.setCustName("传智播客");
                cust.setCustLevel("VIP8");
                cust.setCustSource("百度");
                cust.setCustMobile("10086");
                cust.setCustPhone("911");
                /*创建联系人对象*/
                LinkMan lkm = new LinkMan();
                lkm.setLkm_name("黑马");
                lkm.setLkm_gender("男");
                lkm.setLkm_phone("10010");
                
                //建立级联关系
                /*联系人放客户set集合里面*/
                cust.getSet_LinkMan().add(lkm);
                /*客户放联系人里面*/
                lkm.setCustomer(cust);
                
                //保存到数据库中
                session.save(cust);
                session.save(lkm);
                
                //提交事务
                tx.commit();
                
            }catch(Exception e){
                e.printStackTrace();
                //事务回滚
                tx.rollback();
            }finally{
                //关闭资源
                session.close();//使用openSession()需要手动关闭
                sessionFactory.close();
            }
        }


          第二种,一般都是根据客户加联系人
            在客户映射文件里set标签上进行配置:
            <set name="set_LinkMan" cascade="save-update">
            设置级联保存,cascade就是级联的意思
            在代码中创建两个对象后,把联系人放到客户里面就可以
            底层实现就是复杂写法,配置帮助简化

    //级联的简化写法,开发常用
        @Test
        public void testAdd2(){
            //请将声明放在try之外
            SessionFactory sessionFactory = null;
            Session session = null;
            Transaction tx = null;
            try{
                //得到工厂
                sessionFactory = HibernateUtils.getSessionFactory();
                //得到session
                session = sessionFactory.openSession();
                //开启事务
                tx = session.beginTransaction();
                
                //级联保存操作(较麻烦的方法)
                /*创建客户对象*/
                Customer cust = new Customer();
                cust.setCustName("百度");
                cust.setCustLevel("VIP6");
                cust.setCustSource("搜狗");
                cust.setCustMobile("10000");
                cust.setCustPhone("911");
                /*创建联系人对象*/
                LinkMan lkm = new LinkMan();
                lkm.setLkm_name("白马");
                lkm.setLkm_gender("男");
                lkm.setLkm_phone("10010");
                
                //联系人放客户即可
                cust.getSet_LinkMan().add(lkm);
                //再保存客户即可
                session.save(cust);
                
                //提交事务
                tx.commit();
                
            }catch(Exception e){
                e.printStackTrace();
                //事务回滚
                tx.rollback();
            }finally{
                //关闭资源
                session.close();//使用openSession()需要手动关闭
                sessionFactory.close();
            }
        }

            推荐的是第二种,第一种作为原理理解

      级联删除写法:
        (需要先删完联系人,或把联系人的外键设置为Null)
        根据客户去删除联系人,在客户的映射文件中进行配置
        也是set标签上配置
        <set name="set_LinkMan" cascade="save-update,delete">

        /**
             * 级联删除的操作
             */
            @Test
            public void testDelete(){
                //请将声明放在try之外
                SessionFactory sessionFactory = null;
                Session session = null;
                Transaction tx = null;
                try{
                    //得到工厂
                    sessionFactory = HibernateUtils.getSessionFactory();
                    //得到session
                    session = sessionFactory.openSession();
                    //开启事务
                    tx = session.beginTransaction();
                    
                    //先查后删
                    Customer cust = session.get(Customer.class, 2);
                    session.delete(cust);
                    
                    //提交事务
                    tx.commit();
                    
                }catch(Exception e){
                    e.printStackTrace();
                    //事务回滚
                    tx.rollback();
                }finally{
                    //关闭资源
                    session.close();//使用openSession()需要手动关闭
                    sessionFactory.close();
                }
            }

      一对多的修改操作:
        先查后改:通过公司增加员工与设置员工的所属公司来操作
        但是,Hibernate中有双向维护外键,所以会修改两次外键,这是影响性能的
        解决方案是让其中的一方放弃维护外键(让一对多中的一这一方放弃维护,这里就是客户这一方放弃维护)
        所有人都认识习主席,而习主席不一定认识所有人
        具体操作是通过配置解决 inverse true表示放弃维护,false表示放弃(也就是说inverse表示是否放弃维护)
        <set name="set_LinkMan" cascade="save-update,delete" inverse="true">

    /**
             * 级联修改的操作
             */
            @Test
            public void testUpdate(){
                //请将声明放在try之外
                SessionFactory sessionFactory = null;
                Session session = null;
                Transaction tx = null;
                try{
                    //得到工厂
                    sessionFactory = HibernateUtils.getSessionFactory();
                    //得到session
                    session = sessionFactory.openSession();
                    //开启事务
                    tx = session.beginTransaction();
                    /*先查后改*/
                    Customer cust = session.get(Customer.class, 2);
                    LinkMan lkm = session.get(LinkMan.class, 1);
                    cust.getSet_LinkMan().add(lkm);
                    lkm.setCustomer(cust);
                    //提交事务
                    tx.commit();
                    
                }catch(Exception e){
                    e.printStackTrace();
                    //事务回滚
                    tx.rollback();
                }finally{
                    //关闭资源
                    session.close();//使用openSession()需要手动关闭
                    sessionFactory.close();
                }
            }

    接下来是多对多的操作,有了上面的基础,接下来的就简单一些了

    多对多的配置:
      一般使用的是多对多的级联保存,而比较少用级联删除
        配置过程和很像
          一、创建实体类:用户和角色

          二、两个实体类互相表示
            都是使用Set互相表示
          三、配置映射关系
            包括基本的映射配置和关系映射
          四、在核心配置文件中引入映射文件

      完成后测试:
        运行一下工具类,表出来即正常

      多对多级联保存:
        和一对多很相似
          第一步是配置级联
          第二步是代码实现
        配置用户的级联保存:<set name="set_LinkMan" cascade="save-update">
        把角色放到用户里,保存用户即可完成
        步骤都是创建用户、角色对象,再将角色放入用户,保存用户,详细代码和一对多极其相似,略

      用户:

    package cn.entity02;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class User {
    
        private Integer user_id;//用户ID
        private String user_name;//用户名
        private String user_password;//密码
        //一个用户可以有多个角色
        private Set<Role> set_Role = new HashSet<Role>();
        
        
        public Set<Role> getSet_Role() {
            return set_Role;
        }
    
        public void setSet_Role(Set<Role> set_Role) {
            this.set_Role = set_Role;
        }
    
        public User() {
            
        }
    
        public Integer getUser_id() {
            return user_id;
        }
    
        public void setUser_id(Integer user_id) {
            this.user_id = user_id;
        }
    
        public String getUser_name() {
            return user_name;
        }
    
        public void setUser_name(String user_name) {
            this.user_name = user_name;
        }
    
        public String getUser_password() {
            return user_password;
        }
    
        public void setUser_password(String user_password) {
            this.user_password = user_password;
        }
        
    }

    用户的映射配置文件:

    <?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>
        <!-- 映射的类 name是类的全路径 table是表名 -->
        <class name="cn.entity02.User" table="t_user03">
            <id name="user_id" column="user_id">
                <!-- 设置id增长策略 -->
                <generator class="native"></generator>
            </id>
            <!-- 配置其它属性 -->
            <property name="user_name" column="user_name"></property>
            <property name="user_password" column="user_password"></property>
            <!-- 多对多的配置
            name属性是角色色集合的名称
            table是关系表的名字
             -->
            <set name="set_Role" table="user_role" cascade="save-update">
                <!-- 当前表在第三张表中外键的名字 -->
                <key column="user_id"></key>
                <!-- class为角色的全路径,column为角色在第三张表中的外键 -->
                <many-to-many class="cn.entity02.Role" column="role_id"></many-to-many>
            </set>
        </class>
    </hibernate-mapping>

    角色:

    package cn.entity02;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Role {
    
        private Integer role_id;//角色ID
        private String role_name;//角色名
        private String role_memo;//角色描述
        //一个角色可以有多个用户
        private Set<User> set_User = new HashSet<User>();
        
        
        public Set<User> getSet_User() {
            return set_User;
        }
        public void setSet_User(Set<User> set_User) {
            this.set_User = set_User;
        }
        public Role() {
            
        }
        public Integer getRole_id() {
            return role_id;
        }
        public void setRole_id(Integer role_id) {
            this.role_id = role_id;
        }
        public String getRole_name() {
            return role_name;
        }
        public void setRole_name(String role_name) {
            this.role_name = role_name;
        }
        public String getRole_memo() {
            return role_memo;
        }
        public void setRole_memo(String role_memo) {
            this.role_memo = role_memo;
        }
        
        
    }

    角色的映射文件:

    <?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>
        <!-- 映射的类 name是类的全路径 table是表名 -->
        <class name="cn.entity02.Role" table="t_role">
         <!-- 当前表在第三张表中外键的名字 -->
         <id name="role_id" column="role_id">
                <!-- 设置id增长策略 -->
                <generator class="native"></generator>
            </id>
            <!-- 配置其它属性 -->
            <property name="role_name" column="role_name"></property>
            <property name="role_memo" column="role_memo"></property>
            <!-- 配置和User同理 ,都是主要配置外键信息-->
            <set name="set_User" table="user_role">
                <key column="role_id"></key>
                <many-to-many class="cn.entity02.Role" column="user_id"></many-to-many>
            </set>
        </class>
    </hibernate-mapping>

     这里配置完一定记得去核心配置文件引入资源!

      级联删除也是先配置后删除,但是用的较少,而且代码与一对多极其相似
        就注意一点,删除是需要传入对象的,故一定是先查后删

       多对多时注意维护第三张表
        一般思路都是通过维护第三张表来维护关系
        例如让某个用户有某个角色
        根据id查询到用户和角色
        把角色放到用户里(getSet().add())
        移除则是(getSet.remove())
        这里还是得记得:持久态会更新到数据库

  • 相关阅读:
    一起买beta版PHP单元测试
    Deepin下phpunit安装,以及执行过程中所遇到的问题
    一起买Beta版本系列文档
    一起买之路——测试篇
    模块整合测试
    面向对象进阶6:元类
    面向对象进阶5:上下文管理协议
    面向对象进阶4:软件开发规范
    面向对象进阶3:迭代器协议、描述符
    面向对象进阶2:二次加工标准类型(包装)及一些内置函数
  • 原文地址:https://www.cnblogs.com/jiangbei/p/6736086.html
Copyright © 2020-2023  润新知