美文网首页JAVAEE
JAVAEE框架学习——Hibernate——一对多|多对多关联

JAVAEE框架学习——Hibernate——一对多|多对多关联

作者: So_ProbuING | 来源:发表于2018-05-21 16:28 被阅读18次

    表关系的分析

    数据库中多表之间存在着三种关系:


    表关系

    表与表的三种关系:

    一对多|多对一

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

    一对多

    表中的表达

    表中表达

    实体中的表达

    实体中的表达

    orm元数据表达

    • 多对一配置
     <!--表示多对一-->
            <many-to-one name="customer"
                         column="lkm_cust_id"
                         class="Customer">
    
            </many-to-one>
    

    这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多对一的一的名字,column表示外键的列名 class表示的是多个对一个的一的实体类名

    • 一对多配置
     <!--配置集合 一对多的关系-->
            <set name="linkMens">
                <!--指定外键列名-->
                <key column="lkm_cust_id"></key>
                <!--指定一对多还是多对多
                    class指定是与哪个类型一对多
                -->
                <one-to-many class="LinkMan"/>
            </set>
    

    这里的这个配置表示的是:当前的这个配置对象存在多对一的情况,其中name表示实体中多个的set集合,key column表示外键的列名 class表示的是一对多的多的实体类名

     /**
         *保存客户以及客户下的联系人
         */
        @Test
        public void fun1() {
            //获得session
            Session session = HibernateUtils.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //----------------------------
            //操作数据
            Customer customer = new Customer();
            customer.setCust_name("xxx");
            LinkMan lm1 = new LinkMan();
            lm1.setLkm_name("aaa1");
            LinkMan lm2 = new LinkMan();
            lm2.setLkm_name("aaa2");
            //表达一对多关系 客户下有多个联系人
            customer.getLinkMens().add(lm1);
            customer.getLinkMens().add(lm2);
            //表达多对一 联系人属于哪个客户
            lm1.setCustomer(customer);
            lm2.setCustomer(customer);
    
            //瞬时状态对象---->持久化状态
            session.save(customer);
            session.save(lm1);
            session.save(lm2);
            //----------------------------
            //提交事物
            tx.commit();
            //关闭资源
            session.close();
        }
    
    /**
         * 为客户添加联系人
         */
        @Test
        public void fun2() {
            //获得session
            Session session = HibernateUtils.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //----------------------------
            //操作数据
            //获得要操作的客户对象
            Customer customer = session.get(Customer.class, 1l);
    
            //创建联系人
            LinkMan lm1 = new LinkMan();
            //将联系人添加到客户,将客户设置到联系人中
            customer.getLinkMens().add(lm1);
            lm1.setCustomer(customer);
            //执行保存
            session.save(customer);
            session.save(lm1);
            //----------------------------
            //提交事物
            tx.commit();
            //关闭资源
            session.close();
        }
    
     /**
         * 从客户中删除某个联系人
         */
        @Test
        public void fun3() {
            //获得session
            Session session = HibernateUtils.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //----------------------------
            //操作数据
            //获得客户
            Customer c = session.get(Customer.class, 1l);
            LinkMan lm = session.get(LinkMan.class, 3l);
            //获得客户的联系人
            //将要移除的联系人从客户的联系人集合中移除
            c.getLinkMens().remove(lm);
            //清空联系人的客户
            lm.setCustomer(null);
            //----------------------------
            //提交事物
            tx.commit();
            //关闭资源
            session.close();
        }
    

    上面的代码再删除某个联系人时由于数据都是查询出来的,都是出于持久态的对象,所以再事务进行提交时Hibernate会查看数据是否发生变化,发生变化后会将数据更新,所以不需要执行session.save()方法进行保存数据

    操作进阶

    在上面的操作中,我们在操作保存客户的联系人的时候,我们需要执行多条保存的代码,这样显然很麻烦,我们希望再保存客户的时候,将客户的联系人也一起保存。所以我们需要进行一些别的配置

    • 级联操作
     <!--级联操作:cascade
                    save-update:级联保存更新
                    delete:级联删除
                    all: save-update delete 级联保存更新和级联删除
                级联操作:简化操作
    
            -->
            <set name="linkMens" cascade="all">
                <!--配置集合 一对多的关系-->
                <!--指定外键列名-->
                <key column="lkm_cust_id"></key>
                <!--指定一对多还是多对多
                    class指定是与哪个类型一对多
                -->
                <one-to-many class="LinkMan"/>
    
            </set>
    

    级联操作应用在一对多的多的时候,也可以实现保存联系人的时候将客户保存

    • linkman.hbm.xml 配置级联操作
     <!--表示多对一-->
            <many-to-one name="customer"
                         column="lkm_cust_id"
                         class="Customer"
                         cascade="all"
            >
    

    操作代码

    /**
         * 级联操作 保存联系人 级联保存客户
         */
        @Test
        public void fun5() {
            //获得session
            Session session = HibernateUtils.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //----------------------------
            //操作数据
            //创建客户
            Customer customer = new Customer();
            customer.setCust_name("google company");
    
            //创建联系人
            LinkMan lm = new LinkMan();
            lm.setLkm_name("劈柴哥");
            //为客户添加联系人
            customer.getLinkMens().add(lm);
            //为联系人指定客户
            lm.setCustomer(customer);
            //保存客户----> 只保存联系人 通过级联操作保存客户
            session.saveOrUpdate(lm);
    
            //----------------------------
            //提交事物
            tx.commit();
            //关闭资源
            session.close();
        }
    

    在平时开发中,建议使用save-update 不建议使用delete 级联删除会删除相关的所有数据

    • 关系维护
      我们查看操作时Hibernate产生的SQL语句时发现了如下的语句
      Hibernate执行的语句.png
      我们发现。在保存时,客户和联系人两房都会维护外键的关系,关系会维护两次,这样冗余了。多余的维护关系语句。显然是客户这一端在维护关系。所以我们需要优化这种情况 这就引入了Hibernate在实体类配置的时候提供的配置 inverse
      inverse是为了提高效率
     <!--
                inverse属性:配置关系是否维护
                配置当前的Customer是否维护与LinkMan的关系
                inverse true 的意思就是将关系维护的工作完全交给对方 作为配置方不进行维护关系
                        * 不维护关系无法在删除的时候将外键置为空
                        false(默认值) 意思是将关系维护的工作由配置方来维护
                        原则:无论关系方如何放弃关系,总有一方必须要维护关系
                        一对多关系当中,也只能是一的一方放弃维护,多的一方不能放弃
                    
            -->
            <set name="linkMens" inverse="true">
                <key column="lkm_cust_id"></key>
                    class指定是与哪个类型一对多   
                <one-to-many class="LinkMan"/>
            </set>
    

    tips:在开发中的删除数据,结合inverse 和cascade 实现不同的数据删除策略。以这个demo为例,一个客户可以由多个联系人。在这种情况下 相当于客户和联系人维持着某种关系。我们在有需求删除客户的时候对联系人的删除有两种。
    第一种:删除Customer的时候,将LinkMan的外键置为空,LinkMan数据保留
    这种情况下,如果Customer不维护数据的话是无法操作LinkMan的外键的,所以在这种情况下我们需要让Customer inverse配置为false 让customer维护关系,在这种情况下删除Customer的时候会删除LinkMan的关系。
    第二种:删除Customer的时候,级联操作将LinkMan的数据删除。
    在这种情况下,将Customer inverse设置为true 并且将cascade指定为delete 这样在删除Customer的同时就会删除LinkMan子表中的数据。

    多对多关联关系映射操作

    建表原则:创建一个中间表,中间表中至少两个字段作为外键分别指向多对多双方的主键

    多对多

    表中关系表达

    表中表达

    实体中的表达

    实体中的表达

    orm元数据表达

    • User表配置
      <set name="roles" table="sys_user_role">
                <!--
                    key标签
                        column:当前对象在中间表中的外键名称
                -->
                <key column="user_id"></key>
                <!--
                    many-to-many标签:
                    class:关联另一方的类的全类名
                    column 关联的另一方在中间表的外键名称
                -->
                <many-to-many class="Role" column="role_id"></many-to-many>
            </set>
    
    • role表配置
      <!--Role多对多配置-->
            <set name="users" table="sys_user_role">
                <key column="role_id"/>
                <many-to-many class="User"
                              column="user_id"/>
            </set>
    

    多对多操作

    我们使用案例:保存员工以及角色

    在多对多关系中一定要有一方放弃关系的维护,否则会出现主键重复错误
    放弃关系维护在配置文件中使用 inverse=“true” 表示放弃操作

    • 配置文件
      <!-- inverse 属性 使得一方放弃维护外键关系-->
            <set name="users" table="sys_user_role" inverse="true">
                <key column="role_id"/>
                <many-to-many class="User"
                              column="user_id"/>
            </set>
    
    • 实例代码
     //从工厂获得session
            Session session = HibernateUtil.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //操作数据
            //-------------------------------
            //创建两个User
            User user1 = new User();
            User user2 = new User();
            user1.setUser_name("blackman");
            user2.setUser_name("whiteman");
    
            //创建两个role
            Role role1 = new Role();
            Role role2 = new Role();
            Role role3 = new Role();
            role1.setRole_name("vp");
            role2.setRole_name("cto");
            role3.setRole_name("security");
    
    
            //用户表达关系
            user1.getRoles().add(role1);
            user1.getRoles().add(role2);
            user2.getRoles().add(role1);
            user2.getRoles().add(role3);
            //角色表达关系
            role1.getUsers().add(user1);
            role1.getUsers().add(user2);
            role2.getUsers().add(user1);
            role3.getUsers().add(user2);
            //保存到数据库
            session.save(user1);
            session.save(user2);
            session.save(role1);
            session.save(role2);
            session.save(role3);
            //-------------------------------
            //提交事务
            tx.commit();
            //关闭资源
            session.close();
    
     @Test
        /**
         * 添加角色到某个用户中
         */
        public void addRoleToUser() {
            Session session = HibernateUtil.openSession();
            Transaction tx = session.beginTransaction();
            //操作
            //------------
            //获得要添加的用户
            User user = session.get(User.class, 1l);
            //创建公关角色
            Role role = new Role();
            role.setRole_name("公关");
            //将角色添加到用户
            user.getRoles().add(role);
            //将角色转换为持久化
            session.save(role);
            //------------
            tx.commit();
            session.close();
        }
    
    /**
    * 从指定角色删除
    */
      @Test
        public void delRoleFromUser(){
            //获取session
            Session session = HibernateUtil.openSession();
            //开启事物
            Transaction tx = session.beginTransaction();
            //操作数据
            //-------------
            //获得要删除的数据
            User user = session.get(User.class, 3l);
            //获得要删除的角色数据
            Role role = session.get(Role.class, 7l);
            user.getRoles().remove(role);
            //-------------
            //提交事务
            tx.commit();
            //关闭资源
            session.close();
        }
    
    
    

    进阶操作

    • 级联操作
      cascade: 级联操作
      在User.hbm.xml中配置cascade后
            <set name="roles" table="sys_user_role" cascade="save-update">
    
    

    可以进行级联保存 上面的操作代码不再需要

    session.save(role)
    

    一对一

    • 建表原则:
      • 1 唯一外键对应:假设一对一种的任意一方为多,在多的一方创建外键指向一的一方的主键,然后将外键设置为唯一。
      • 2 主键对应:一方的主键作为另一方的主键


        一对一

    相关文章

      网友评论

        本文标题:JAVAEE框架学习——Hibernate——一对多|多对多关联

        本文链接:https://www.haomeiwen.com/subject/qmkpdftx.html