一对多映射(重点)

编写JavaBean

  1. 1的一方

    • 保存n的一方对象的Set集合
    • hibernate框架默认的集合是set集合,集合必须要手动初始化
    • 案例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      public class Customer {
      private Long cust_id; //客户编号(主键)
      private String cust_name; //客户名称(公司名称)
      private Long cust_user_id; //负责人id
      private Long cust_create_id; //创建人id
      private String cust_source; //客户信息来源
      private String cust_industry; //客户所属行业
      private String cust_level; //客户级别
      private String cust_linkman; //联系人
      private String cust_phone; //固定电话
      private String cust_mobile; //移动电话
      //1的一方
      //hibernate框架默认的集合是set集合,集合必须要手动初始化
      private Set<LinkMan> LinkMans = new HashSet<LinkMan>();
      set&get...
      }
  2. n的一方

    • 保存1的一方的对象
    • 这个对象千万不要自己new,配置文件提供该类的全路径,hibernate框架会自己new
    • 案例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      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;
      //n的一方
      //编写一个对象,千万不要自己new,配置文件提供类的全路径,hibernate框架会自己new
      private Customer customer;
      set&get...
      }

编写一对多的配置文件

  1. 1的一方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?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>
    <!-- 配置类和表结构的映射 -->
    <class name="com.lousen.domain.Customer" table="cst_customer">
    <!-- 配置id,name:JavaBean,column:表结构的字段 -->
    <id name="cust_id" column="cust_id">
    <!-- 主键生成策略 -->
    <generator class="native"></generator>
    </id>
    <!-- 配置其他属性 -->
    <property name="cust_name" column="cust_name"/>
    <property name="cust_user_id" column="cust_user_id"/>
    <property name="cust_create_id" column="cust_create_id"/>
    <property name="cust_source" column="cust_source"/>
    <property name="cust_industry" column="cust_industry"/>
    <property name="cust_level" column="cust_level"/>
    <property name="cust_linkman" column="cust_linkman"/>
    <property name="cust_phone" column="cust_phone"/>
    <property name="cust_mobile" column="cust_mobile"/>
    <!-- 配置一的一方,name:表示集合属性的名称,cascade:级联 -->
    <set name="LinkMans" inverse="true">
    <!-- column:外键字段 -->
    <!-- class:集合属性的全路径 -->
    <key column="lkm_cust_id"></key>
    <one-to-many class="com.lousen.domain.LinkMan"/>
    </set>
    </class>
    </hibernate-mapping>
  2. n的一方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?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>
    <!-- 配置类和表结构的映射 -->
    <class name="com.lousen.domain.LinkMan" table="cst_linkman">
    <!-- 配置id,name:JavaBean,column:表结构的字段 -->
    <id name="lkm_id" column="lkm_id">
    <!-- 主键生成策略 -->
    <generator class="native"></generator>
    </id>
    <!-- 配置其他属性 -->
    <property name="lkm_name" column="lkm_name"/>
    <property name="lkm_gender" column="lkm_gender"/>
    <property name="lkm_phone" column="lkm_phone"/>
    <property name="lkm_mobile" column="lkm_mobile"/>
    <property name="lkm_email" column="lkm_email"/>
    <property name="lkm_qq" column="lkm_qq"/>
    <property name="lkm_position" column="lkm_position"/>
    <property name="lkm_memo" column="lkm_memo"/>
    <!--
    先配置多的一方
    name: 当前JavaBean中的属性
    class:该属性的全路径
    column:外键字段
    cascade: 级联
    -->
    <many-to-one name="customer" class="com.lousen.domain.Customer" column="lkm_cust_id" cascade="save-update"></many-to-one>
    </class>
    </hibernate-mapping>

双向关联数据

  1. 简介
    顾客关联联系人,联系人同时也要关联顾客,两者的对象都需要保存。
  2. 双向关联数据保存
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    /**
    * 双向关联保存数据(最麻烦的)
    */
    @Test
    public void run1(){
    Session session = HibernateUtils.getSession();
    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);
    session.save(l1);
    session.save(l2);
    tr.commit();
    }

级联保存

  1. 简介
    保存一方的同时可以把关联的对象也保存到数据库中,具有方向性,可以修改顾客的配置文件使顾客级联联系人,也可以修改联系人的配置文件使联系人级联顾客。
  2. 配置文件
    在配置文件添加属性cascade="save-update"即可
  3. 级联保存案例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /**
    * 级联保存(单向关联)
    * 顾客级联联系人
    */
    @Test
    public void run2(){
    Session session = HibernateUtils.getSession();
    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);
    //保存数据
    session.save(c1);
    tr.commit();
    }

级联删除(谨慎使用,很少使用)

  1. 简介
    • 普通的直接删除
      删除顾客以后,该顾客对应的联系人的外键那一列的值变为null
    • 级联删除
      删除顾客以后,该顾客对应的联系人的所有数据被删除
  2. 配置文件
    在配置文件添加属性cascade="delete"即可
  3. 级联删除案例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 级联删除
    * 同样具有方向性,也可两边都级联(很危险,相关数据全删)
    * 在联系人和顾客两边都加上级联删除
    * 删除联系人时联系人级联删除顾客,删除顾客时顾客又级联删除联系人
    */
    @Test
    public void run4(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //删除主键为3的联系人
    LinkMan linkman = session.get(LinkMan.class,3L);
    session.delete(linkman);
    tr.commit();
    }

孤儿删除

  1. 简介
    • 只存在于一对多
    • 在一对多的关系中,可以将一的一方认为是父方。将多的一方认为是子方。孤儿删除:在解除了父子关系的时候。将子方整条记录直接删除。
  2. 配置文件
    在配置文件<set>标签上添加属性cascade="delete-orphan"即可
    3.孤儿删除案例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    * 孤儿删除(在解除关系的基础上修改了配置文件,cascade="delete-orphan")
    * 删除整条记录
    */
    @Test
    public void run6(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    LinkMan linkman = session.get(LinkMan.class,9L);
    Customer customer = session.get(Customer.class,9L);
    //解除关系
    customer.getLinkMans().remove(linkman);
    tr.commit();
    }

放弃外键的维护

  1. 简介
    • 双方都维护外键的时候,会产生多余的SQL语句
    • 产生的原因:session的一级缓存中的快照机制,会让双方都更新数据库,产生了多余的SQL语句。
    • 如果不想产生多余的SQL语句,那么需要一方来放弃外键的维护
  2. 配置文件
    <set>标签上配置一个inverse="true"。默认值是false。

cascade和inverse

  1. cascade用来级联操作(保存、修改和删除)
  2. inverse用来维护外键的
  3. 两者一般不能同时存在同一个配置文件
    • inverse在一对多中必须放在1的一方,在多对多时放在少的一方
    • cascade放在inverse的另一方
    • 如果同时存在同一个配置文件,将无法对该方的外键进行保存等操作,因为这一方已经放弃了对外键的维护。

多对多映射(重点)

编写JavaBean

同(一对多)中多的一方
案例:

1
2
3
4
5
6
7
8
9
10
public class User {
private Long uid;
private String username;
private String password;
private Set<Role> roles = new HashSet<Role>();
set&get...
}

1
2
3
4
5
6
7
8
9
public class Role {
private Long rid;
private String rname;
private Set<User> users = new HashSet<User>();
set&get...
}

编写多对多的配置文件

  1. 多-用户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?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>
    <!-- 配置类和表结构的映射 -->
    <class name="com.lousen.domain.User" table="sys_user">
    <!-- 配置id,name:JavaBean,column:表结构的字段 -->
    <id name="uid" column="uid">
    <!-- 主键生成策略 -->
    <generator class="native"></generator>
    </id>
    <!-- 配置其他属性 -->
    <property name="username" column="username"/>
    <property name="password" column="password"/>
    <!--
    配置多对多
    name:集合的名称
    table:中间表的名称
    -->
    <set name="roles" table="sys_user_role" cascade="save-update">
    <!-- 当前对象在中间表外键的名称 -->
    <key column="uid" />
    <!--
    class:集合中所存对象的全路径
    column:集合中所存对象在中间表的外键名称
    -->
    <many-to-many class="com.lousen.domain.Role" column="rid"></many-to-many>
    </set>
    </class>
    </hibernate-mapping>
  2. 多-角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?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>
    <!-- 配置类和表结构的映射 -->
    <class name="com.lousen.domain.Role" table="sys_role">
    <!-- 配置id,name:JavaBean,column:表结构的字段 -->
    <id name="rid" column="rid">
    <!-- 主键生成策略 -->
    <generator class="native"></generator>
    </id>
    <!-- 配置其他属性 -->
    <property name="rname" column="rname"/>
    <!--
    配置多对多
    name:集合的名称
    table:中间表的名称
    -->
    <set name="users" table="sys_user_role" inverse="true">
    <!-- 当前对象在中间表外键的名称 -->
    <key column="rid" />
    <!--
    class:集合中所存对象的全路径
    column:集合中所存对象在中间表的外键名称
    -->
    <many-to-many class="com.lousen.domain.User" column="uid"></many-to-many>
    </set>
    </class>
    </hibernate-mapping>
  3. 中间表
    在多对多中,只要正确配置了文件,Hibernate框架会自动生成中间表

双向关联

多对多进行双向关联的时候:必须有一方放弃外键维护权,否则会报错!!!

级联操作

  1. 简介
    同一对多
  2. 案例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
    * 多对多
    * role放弃外键维护
    * 保存user时级联保存role
    */
    @Test
    public void run1(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //创建用户
    User u1 = new User();
    u1.setUsername("张三");
    User u2 = new User();
    u2.setUsername("李四");
    //创建角色
    Role r1 = new Role();
    r1.setRname("上校");
    Role r2 = new Role();
    r2.setRname("下士");
    //级联保存
    u1.getRoles().add(r1);
    u1.getRoles().add(r2);
    u2.getRoles().add(r1);
    session.save(u1);
    session.save(u2);
    tr.commit();
    }

维护中间表

  1. 简介
    • 在多对多中,只要正确配置了文件,Hibernate框架会自动生成中间表
    • 通过操作对应集合来操作对应的外键
  2. 案例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 维护中间表
    * 通过操作集合
    */
    @Test
    public void run2(){
    Session session = HibernateUtils.getSession();
    Transaction tr = session.beginTransaction();
    //查询李四用户
    User u = session.get(User.class, 2L);
    //查询角色
    Role r = session.get(Role.class, 2L);
    //从中间表移除该条数据
    u.getRoles().remove(r);
    tr.commit();
    }