• Hibernate 中一对多和多对多映射


    1. 一对多映射

    1.1 JavaWeb 一对多建表原则

    • 多方表的外键指向一方表的主键;

    1.2 编写一对多的 JavaBean

    // 客户(一方)和联系人(多方)
        // 客户(一方) JavaBean
        public class Customer{
            private Long cust_id;
            private String cust_name;
            private Long cust_create_id;
            ...
    
            // 关联多方
            // Hibernate 框架默认的集合是 Set 集合,该集合必须要自己手动初始化
            private Set<Linkman> linkmans = new HashSet<Linkman>();
    
            ......
        }
    
        // 联系人(多方) JavaBean
        public class Linkman{
            private Long lkm_id;
            private String lkm_name;
            private String lkm_gender;
            ...
    
            // 关联一方
            // 编写一个对象, 不需要自己 new
            private Customer customer;
    
            ....
        }

    1.3 编写一对多的映射配置文件

    // Customer.hbm.xml (客户映射配置)
        <class name="com.itheima.domain.Customer" table="cst_customer">
            <id name="cust_id" column="cust_id">
                <generator class="native"/>
            </id>
    
            <property name="cust_name" column="cust_name"/>
            <property name="cust_create_id" column="cust_create_id"/>
            ...
    
            // 关联的配置(一方)
            // name  表示集合的名称
            <set name="linkmans">
                <key column="lkm_cust_id"/>  // column 外键的字段
                <one-to-many class="com.itheima.domain.Linkman"/>
            </set>
        </class>
    
    
    // Linkman.hbm.xml (联系人映射配置)
        <class name="com.itheima.doamin.Linkman" table="cst_linkman">
            <id name="lkm_id" column="lkm_id">
                <generator class="native"/>
            </id>
    
            <property name="lkm_name" column="lkm_name"/>
            <property name="lkm_gender" column="lkm_gender"/>
            ...
    
            // 关联的配置(多方)
            //  name    当前 JavaBean 中的属性
            //  class   属性的全路径
            //  column  外键的字段(表)
            <many-to-one name="customer"  class="com.itheima.domain.Customer" column="lkm_cust_id"/>
        </class>

    2. 保存客户和联系人的数据

    2.1 双向关联的方式保存数据

     public class Demo{
        public void fun(){
            Session session = HibernateUtils.getCurrentSession();
            Transaction tr = session.beginTransaction();
    
            // 创建客户
            Customer c1 = new Customer();
            c1.setCust_name("张三");
    
            // 创建联系人
            Linkman lk1 = new Linkman();
            lk1.setLkm_name("熊大");
    
            Linkman lk2 = new Linkman();
            lk2.setLkm_name("熊二");
    
            // 双向关联
            c1.getLinkmans().add(lk1);
            c1.getLinkmans().add(lk2);
    
            lk1.setCustomer(c1);
            lk2.setCustomer(c1);
    
            session.save(c1);
            session.save(lk1);
            session.save(lk2);
    
            tr.commit();
        }
    }

    2.2 级联保存

    • 级联保存:保存一方,同时可以把关联的对象也保存到数据库中!
    • 级联保存是有方向性的.例如,保存客户时,级联保存联系人;或者,保存联系人时,级联保存客户;
    • 使用 cascade="save-update"
    // 保存客户,级联保存联系人
        // 需要在客户 Customer.hbm.xml 中配置
        <set name="linkmans" cascade="save-update">
            <key column="lkm_cust_id"/>
            <one-to-many class="com.itheima.domain.Linkman"/>
        </set>
    
        pulic class Demo{
    
            pulic class fun(){
                Session session = HibernateUtils.getCurrentSession();
                Transaction tr = session.beginTransaction();
    
                // 创建客户
                Customer c1 = new Customer();
                c1.setCust_name("张三");
    
                // 创建联系人
                Linkman l1 = new Linkman();
                l1.setLkm_name("熊大");
    
                Linkman l2 = new Linkman();
                l2.setLkm_name("熊二");
    
                // 单向关联
                c1.getLinkmans().add(l1);
                c1.getLinkmans().add(l2);
    
                l1.setCustomer(c1); // 此处,如果没有,报错.
                l2.setCustomer(c1); // 此处,如果没有,报错.
    
                // 保存客户
                session.save(c1);
    
                tr.commit();
            }
        }
    
    
    // 保存联系人,级联保存客户
        // 需要在联系人 Linkman.hbm.xml 中配置
        <many-to-one name="customer" class="cn.itheima.domain.Customer"
                        column="lkm_cust_id" cascade="save-update"/>
    
        public class Demo{
            public class fun(){
                Session session = HibernateUtils.getCurrentSession();
                Transaction tr = session.beginTransaction();
    
                // 创建客户
                Customer c1 = new Customer();
                c1.setCust_name("张三");
    
                // 创建联系人
                Linkman l1 = new Linkman();
                l1.setLkm_name("熊大");
    
                Linkman l2 = new Linkman();
                l2.setLkm_name("熊二");
    
                l1.setCustomer(c1);
                l2.setCustomer(c2);
    
                // 保存联系人
                session.save(l1);
                session.save(l2);
    
                tr.commit();
            }
        }

    2.3 级联删除

    • 级联删除也是有方向性的;
    // 删除客户时, 删除联系人(一方级联删除多方), 配置 Customer.hbm.xml
        <set name="linkmans" cascade="delete">
            <key column="lkm_cust_id"/>
            <one-to-many class="cn.itheima.domain.Linkman"/>
        </set>

    2.4 级联的取值和孤儿删除

    1. 级联的取值(cascade 的取值)
    • none: 不使用级联;
    • save-update: 级联保存或更新;
    • delete: 级联删除;
    • delete-orphan: 孤儿删除(只能应用在一对多关系);
    • all: 除了 delete-orphan 的所有情况;
    • all-delete-orphan: 包含了 delete-orphan 的所有情况;
    2. 孤儿删除
    • 只有在一对多的环境下才有孤儿删除;
    • 在一对多的关系中,可以将一方认为是父方,将多的一方认为是子方;孤儿删除,就是在解除了父子关系的时候,
      将子方记录直接删除;

    2.5 让某一方放弃外键的维护,为多对多映射做准备: inverse

    1. 在修改客户和联系人的关系时,进行双向关联,双方都会维护外键,会产生多余的 SQL 语句.
      • 产生的原因: session 的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的 SQL 语句.
    2. 如果不想产生多余的 SQL 语句,那么需要一方来放弃外键的维护, 由多方来维护!
    // 放弃外键维护
        // Customer.hbm.xml 进行如下配置
        <set name="linkman" inverse="true">  // true 表示放弃; 默认值为 false
            <key column="lkm_cust_id"/>
            <one-to-many class="com.itheima.domain.Linkman"/>
        </set>

    3. 多对多映射

    3.1 多对多建表原则

    • 以用户和角色为例,需要创建三张表: 用户表,角色表,中间表(维护前面两个表的数据);
    • 使用Hibernate 框架,只要编写两个 JavaBean 以及对应的映射配置文件,中间表会自动生成;
    • 多对多映射关系中,必须有一方放弃外键维护;

    // 编写用户和角色的 JavaBean
        // 用户的 JavaBean
        public class User{
            private Long user_id;
            private String user_name;
            private String user_pwd;
            ...
    
            private Set<Role> roles = new HashSet<Role>();
        }
    
        // 角色的 JavaBean
        public class Role{
            private Long role_id;
            private String role_name;
            private String role_desc;
    
            private Set<User> users = new HashSet<User>();
        }
    
    
    // 编写映射配置文件
        // 用户的映射配置文件, User.hbm.xml
        <class name="com.itheima.domain.User" table="sys_user">
            <id name="user_id" column="user_id">
                <generator class="native"/>
            </id>
    
            <property name="user_name" column="user_name"/>
            <property name="user_pwd" column="user_pwd"/>
    
            // 配置多对多
            // name 表示集合的名称
            // table 表示中间表的名称
            <set name="roles" table="sys_user_role" inverse="true">
                // 当前对象在中间表的外键名称
                <key column="user_id"/>
    
                // class : 集合中存入的对象,对象的全路径
                // column: 集合中对象,在中间表的外键名称
                <many-to-many class="com.itheima.domain.Role" column="role_id"/>
            </set>
    
        </class>
    
        // 角色的映射配置文件, Role.hbm.xml
        <class name="com.itheima.domain.Role" table="sys_role">
            <id name="role_id" column="role_id">
                <generator class="native"/>
            </id>
    
            <property name="role_name" column="role_name"/>
            <property name="role_desc" column="role_desc"/>
    
            <set name="users" table="sys_user_role">
                <key column="role_id"/>
                <many-to-many class="com.itheima.domain.User" column="user_id"/>
            </set>
        </class>    
  • 相关阅读:
    ListView Item 里多种点击事件的用法
    dialog 设置maxHeight 最大高度
    php的json_encode不兼容JSON_UNESCAPED_UNICODE的解决方案
    java面试一定会遇到的56个面试题
    使用LinearLayout实现ListView,解决ListView和ScrollView滚动冲突
    检测是否是小米手机
    Android 自定义ViewGroup 实战篇 -> 实现FlowLayout
    clone分支,修改文件本地commit后, push回原分支失败,处理方法
    SQL语句往Oracle数据库中插入日期型数据(to_date的用法)
    C# TimeSpan 计算时间差(时间间隔)
  • 原文地址:https://www.cnblogs.com/Jeely/p/11226422.html
Copyright © 2020-2023  润新知