Hibernate表关系03

Posted Sue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate表关系03相关的知识,希望对你有一定的参考价值。

一. 一对多映射

1.基本应用

1.1 准备项目

  • 创建项目:hibernate-02-relation

  • 引入jar,同前一个项目

  • 复制实体(客户)、映射、配置、工具类

1.2 创建订单表

表名: t_order

语句

1 CREATE TABLE `t_order` (
2   `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT \'id\',
3   `orderno` varchar(20) DEFAULT NULL COMMENT \'订单编号\',
4   `product_name` varchar(100) DEFAULT NULL COMMENT \'商品名称\',
5   `customer_id` bigint(20) DEFAULT NULL COMMENT \'客户id\',
6   PRIMARY KEY (`id`),
7   KEY `order_customer_fk` (`customer_id`),
8   CONSTRAINT `order_customer_fk` FOREIGN KEY (`customer_id`) REFERENCES `t_customer` (`c_id`)
9 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
View Code

1.3 创建Order实体类

 1 /**
 2  * 订单(多方)
 3  */
 4 public class Order {
 5     private Long id;
 6     private String orderno;
 7     private String productName;
 8 
 9     //关联客户
10     private Customer customer;
11     
12     //getter seter toString
13 }
View Code

1.4 修改Customer实体类

添加关联订单

 1 /**
 2  * 客户(一方)
 3  */
 4 public class Customer{
 5     private Long id;
 6     private String name;
 7     private Character gender;
 8     private Integer age;
 9     private String level;
10     
11     //关联订单
12     private Set<Order> orders = new HashSet<Order>();
13     //getter setter toString
14 }
View Code

1.5 Customer配置一对多

   <class name="Customer" table="t_customer">
         ......
         <!-- 一对多配置 -->
         <set name="orders">
             <!-- 外键字段名称 -->
             <key column="customer_id"></key>
             <one-to-many class="Order"/>
         </set>
     </class>
View Code

1.6 Order配置多对一

 1 <hibernate-mapping package="com.qfedu.hibernate.pojo.one2many">
 2  
 3      <class name="Order" table="t_order">
 4          <id name="id" column="id">
 5              <generator class="native"></generator>
 6          </id>
 7          <property name="orderno" column="orderno"></property>
 8          <property name="productName" column="product_name"></property>
 9          
10          <!-- 多对一配置 
11              name javaBean中的属性
12              class 属性的全路径
13              colunm 对应的列名
14              -->
15         -->
16          <many-to-one name="customer" class="com.itqf.domain.Customer" column="customer_id" />
17      </class>
18 </hibernate-mapping> 
View Code

1.7 将映射文件加入hibernate.cfg.xml

1 <mapping resource="/pojo/one2many/Customer.hbm.xml"/>
2 <mapping resource="/pojo/one2many/Order.hbm.xml"/>
View Code

1.8 测试新增关联数据

 1 public class One2manyTest {
 2     /**
 3      * 需求:1个客户 2张订单
 4      */
 5     @Test
 6     public void testCreateOrder(){
 7         //准备数据
 8         Customer cust = new Customer();
 9         cust.setName("海伦");
10         cust.setGender(\'女\');
11         cust.setAge(18);
12         cust.setLevel("VIP");
13       
14         Order o1 = new Order();
15         o1.setOrderno("201709070001");
16         o1.setProductName("JavaWeb开发详解");
17       
18         Order o2 = new Order();
19         o2.setOrderno("201709070002");
20         o2.setProductName("Spring开发详解");
21       
22         Session session = HibernateUtil.openSession();
23         Transaction tx = session.beginTransaction();
24         
25         //建立一对多双向关系
26         cust.getOrders().add(o1);
27         cust.getOrders().add(o2);
28       
29         o1.setCustomer(cust);
30         o2.setCustomer(cust);
31       
32         session.save(cust);
33         session.save(o1);
34         session.save(o2);
35       
36         tx.commit();
37         session.close();
38     }
39 }
View Code

1.9 测试查询订单

 1 /**
 2    * 查询操作
 3    */
 4  @Test
 5  public void testSearch(){   
 6      Session session = HibernateUtil.openSession();
 7      Transaction tx = session.beginTransaction();
 8         
 9      //查询一个客户,关联查询订单
10      Customer cust = session.get(Customer.class, 3L);
11      System.out.println(cust.getName()+"的订单:");
12      Set<Order> orders = cust.getOrders();
13      for (Order order : orders) {
14          System.out.println(order.getOrderno()+","+order.getProductName());
15      }
16         
17      tx.commit();
18      session.close();
19   }
View Code
2.cascade级联操作

2.1. 测试级联保存

当只保存双向关联关系的一方时,会报告错误,此时应该在customer中配置级联保存

级联操作:就是操作一个对象的时候,想同时操作它的关联对象。

修改映射文件

<set name="orders" cascade="save-update">

如下用例,可以先测试查看报错信息;再配置上面的级联保存,然后再次进行测试,成功。

 1   /**
 2      * 保存操作 - 级联保存
 3      */
 4     @Test
 5     public void testCascadeSave(){
 6         //准备数据
 7         Customer cust = new Customer();
 8         cust.setName("海伦");
 9         cust.setGender(\'女\');
10         cust.setAge(18);
11         cust.setLevel("VIP");
12         
13         Order o1 = new Order();
14         o1.setOrderno("201709070001");
15         o1.setProductName("JavaWeb开发详解");
16         
17         Order o2 = new Order();
18         o2.setOrderno("201709070002");
19         o2.setProductName("Spring开发详解");
20         
21         Session session = HibernateUtil.openSession();
22         Transaction tx = session.beginTransaction();
23         
24         //建立一对多单向关联
25         cust.getOrders().add(o1);
26         cust.getOrders().add(o2);
27         //o1.setCustomer(cust);
28         //o2.setCustomer(cust);
29       
30         session.save(cust);//使用级联保存 (  想保存客户的时候,同时保存订单 )
31         //session.save(o1);//设置级联保存后不用保存订单
32         //session.save(o2);
33         
34         tx.commit();
35         session.close();
36     }
View Code

2.2 测试级联删除

当只删除父记录时,在删除客户的时候,Hibernate会把订单表的外键值置空,此时可以配置级联删除

<set name="orders" cascade="save-update,delete">

测试代码

 1    /**
 2      * 级联删除
 3      * 注意:
 4      *  1)如果没有级联删除,那么在删除客户的时候,会把订单表的cust_id外键值设置为null
 5      *  2)有了级联删除,那么在删除客户的时候,会同时把该客户的所有订单删除
 6      */
 7     @Test
 8     public void testCascadeDelete(){
 9         //准备数据
10         
11         Session session = HibernateUtil.openSession();
12         Transaction tx = session.beginTransaction();
13         
14         Customer cust = session.get(Customer.class, 4L);
15         session.delete(cust);
16         
17         tx.commit();
18         session.close();
19     }
View Code

3、inverse关系反转

3.1、分析前面的测试

  1. 运行级联保存的测试用例

  2. 查看日志中的sql语句

插入一个用户、两个订单,应该执行3个insert语句

但是发现日志中多打印了两个update语句

默认情况下inverse的值是false:

<set name="orders" cascade="all" inverse="false">

表示customer 一方需要维护关联关系,因此需要维护外键,有关联记录生成时,会做外键的更新操作。

而这个更新操作是没有必要的,因为order插入的时候已经将外键值插入。

所以customer中的update的语句是多余的

3.2、优化

inverse 配置:表示是否把关联关系的维护权反转(放弃)

false:默认值,不反转(不放弃)

true:反转(放弃)

放弃customer方的外键维护

<set name="orders" cascade="all" inverse="true">

重新测试,发现只有三条insert语句

3.3、也可以保存订单

step1:保存时,保存订单

1  //建立一对多单向关联
2  //cust.getOrders().add(o1);
3  //cust.getOrders().add(o2);
4    o1.setCustomer(cust);
5    o2.setCustomer(cust);
6  //session.save(cust);//使用级联保存 (  想保存客户的时候,同时向保存订单 )
7    session.save(o1);
8    session.save(o2);
View Code

step2:在订单端设置级联保存

<!-- 多对一配置 -->
<many-to-one name="customer" class="Customer" column="customer_id" cascade="all"/>

3.4、结论

通常在一对多的关联配置中,多方无法放弃关系维护权,所以应该放弃 1 方的维护权,意味着在 1 方加上 inverse=true 配置

二. 多对多映射

需求: 用户与角色是多对多的关系

1.基本配置

1.1 创建User实体类

1 public class User{
2     
3     private Integer id;
4     private String name;
5     
6     //关联角色
7     private Set<Role> roles = new HashSet<Role>();
8 }
View Code

1.2 创建Role实体类

1 public class Role{
2     private Integer id;
3     private String name;
4     
5     //关联用户
6     private Set<User> users = new HashSet<User>();
7 }
View Code

1.3 User映射配置

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC 
 3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 5     
 6 <hibernate-mapping package="com.qfedu.hibernate.pojo.many2many">
 7  
 8      <class name="User" table="t_user">
 9          <id name="id" column="id">
10              <generator class="native"></generator>
11          </id>
12          <property name="name" column="name"></property>
13          
14          <!-- 多对多映射 -->
15          <!-- 
16              table:中间表名
17           -->
18          <set name="roles" table="t_user_role" >
19              <!-- 当前方在中间表的外键 -->
20              <key column="user_id"/>
21              <!-- column:对方在中间表的外键 -->
22              <many-to-many class="Role" column="role_id"/>
23          </set>
24      </class>
25 </hibernate-mapping> 
View Code

1.4 Role配置

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC 
 3     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 4     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 5     
 6 <hibernate-mapping package="com.qfedu.hibernate.pojo.many2many">
 7  
 8      <class name="Role" table="t_role">
 9          <id name="id" column="id">
10              <generator class="native"></generator>
11          </id>
12          <property name="name" column="name"></property>
13          
14          <!-- 多对多映射 -->
15          <!-- 
16              table:中间表名
17           -->
18          <set name="users" table="t_user_role" >
19              <!-- 当前方在中间表的外键 -->
20              <key column="role_id"/>
21              <!-- column:对方在中间表的外键 -->
22              <many-to-many class="User" column="user_id"/>
23          </set>
24      </class>
25  
26 </hibernate-mapping>  
View Code

1.5 核心配置文件添加映射路径

1   <mapping resource="/pojo/many2many/User.hbm.xml"/>
2   <mapping resource="/pojo/many2many/Role.hbm.xml"/>
View Code

1.6、测试增加

注意:以下测试用例如果直接执行,会报告联合主键插入重复的错误。因此可以在任意一方设置inverse选项=true

<set name="users" table="t_user_role" inverse="true">

测试代码:

 1 public class Many2manyTest {
 2     /**
 3      * 需求:创建一个用户一个角色
 4      */
 5     @Test
 6     public void testCreateUser() {
 7         
 8         User u1 = new User();
 9         u1.setName("Helen1");
10         
11         Role r1 = new Role();
12         r1.setName("超级管理员1");
13         
14         u1.getRoles().add(r1);
15         r1.getUsers().add(u1);
16         
17         Session session = HibernateUtil.openSession();
18         Transaction tx = session.beginTransaction();
19         //双向都保存
20         session.save(u1);
21         session.save(r1);
22         
23         tx.commit();
24         session.close();
25     }
26 }
View Code

4.7、级联保存

注意:在多对多的保存中,如果不设置级联保存,也不设置inverse="true",那么会报告联合主键重复的错误。

可以设置级联保存,在User的多对多关联中设置如下:

<set name="roles" table="t_user_role" cascade="save-update">

测试代码:

 1 public class Many2manyTest {
 2     /**
 3      * 需求:创建一个用户一个角色
 4      */
 5     @Test
 6     public void testCreateUser() {
 7         
 8         User u1 = new User();
 9         u1.setName("Helen1");
10         
11         Role r1 = new Role();
12         r1.setName("超级管理员1");
13         
14         u1.getRoles().add(r1);
15         //r1.getUsers().add(u1);
16         
17         Session session = HibernateUtil.openSession();
18         Transaction tx = session.beginTransaction();
19         
20         session.save(u1);
21         //session.save(r1);
22         
23         tx.commit();
24         session.close();
25     }
26 }
View Code

4.8、级联删除

当没有设置级联删除的时候,如果删除User表中的记录,那么只删除User表和关联表中的记录

当设置了级联删除的时候,如果删除User表中的记录,那么会将User表、关联表和Role表中的记录全部删除!

<set name="roles" table="t_user_role" cascade="save-update,delete">

测试:

 1     @Test
 2     public void testCascadeDelete() {
 3         
 4         Session session = HibernateUtil.openSession();
 5         Transaction tx = session.beginTransaction();
 6         
 7         User u = session.get(User.class, 6);
 8         session.delete(u);
 9         
10         tx.commit();
11         session.close();
12     }
View Code

三. 一对一映射的两种设计方案

需求:公民表和身份证表是一对一的关系

设计表的两种方案:

1. 一对一唯一外键关联

1.1 创建持久化类

Person

1 public class Person {
2     private Integer id;
3     private String name;
4     
5     //关联身份证
6     private Card card;
7 }
View Code

Card

1 public class Card {
2     private Integer id;
3     private String cardno;
4     
5     //关联公民
6     private Person person;
7 }
View Code

1.2 配置映射文件

Person.hbm.xml

<hibernate-mapping package="pojo.one2one_fk">
 
     <class name="Person" table="t_person">
         <id name="id" column="id">
             <generator class="native"></generator>
         </id>
         <property name="name" column="name"></property>
         
         <!-- 一对一映射 -->
         <one-to-one name="card" class="Card" />
     </class> 
 
</hibernate-mapping>
View Code

Card.hbm.xml

 1 <hibernate-mapping package="pojo.one2one_fk">
 2  
 3      <class name="Card" table="t_card">
 4          <id name="id" column="id">
 5              <generator class="native"></generator>
 6          </id>
 7          <property name="cardno" column="cardno"></property>
 8          
 9          <!-- 唯一外键(一对一) -->
10          <many-to-one name="person" class="Person" column="person_id" unique="true" />
11      </class> 
12  
13 </hibernate-mapping> 
View Code

1.3 核心配置

1    <mapping resource="/pojo/one2one_fk/Person.hbm.xml"/>
2    <mapping resource="/pojo/one2one_fk/Card.hbm.xml"/>
View Code

1.4 测试