Fork me on GitHub

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>    

参考资料

posted @ 2017-10-21 08:28  小a的软件思考  阅读(4324)  评论(0编辑  收藏  举报