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
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 }
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 }
1.5 Customer配置一对多
<class name="Customer" table="t_customer"> ...... <!-- 一对多配置 --> <set name="orders"> <!-- 外键字段名称 --> <key column="customer_id"></key> <one-to-many class="Order"/> </set> </class>
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>
1.7 将映射文件加入hibernate.cfg.xml
1 <mapping resource="/pojo/one2many/Customer.hbm.xml"/> 2 <mapping resource="/pojo/one2many/Order.hbm.xml"/>
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 }
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 }
当只保存双向关联关系的一方时,会报告错误,此时应该在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 }
2.2 测试级联删除
<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 }
-
运行级联保存的测试用例
-
查看日志中的sql语句
插入一个用户、两个订单,应该执行3个insert语句
但是发现日志中多打印了两个update语句
默认情况下inverse的值是false:
<set name="orders" cascade="all" inverse="false">
而这个更新操作是没有必要的,因为order插入的时候已经将外键值插入。
所以customer中的update的语句是多余的
3.2、优化
inverse 配置:表示是否把关联关系的维护权反转(放弃)
false:默认值,不反转(不放弃)
<set name="orders" cascade="all" inverse="true">
重新测试,发现只有三条insert语句
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);
step2:在订单端设置级联保存
<!-- 多对一配置 -->
<many-to-one name="customer" class="Customer" column="customer_id" cascade="all"/>
3.4、结论
通常在一对多的关联配置中,多方无法放弃关系维护权,所以应该放弃 1 方的维护权,意味着在 1 方加上 inverse=true
1 public class User{ 2 3 private Integer id; 4 private String name; 5 6 //关联角色 7 private Set<Role> roles = new HashSet<Role>(); 8 }
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 }
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>
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>
1.5 核心配置文件添加映射路径
1 <mapping resource="/pojo/many2many/User.hbm.xml"/> 2 <mapping resource="/pojo/many2many/Role.hbm.xml"/>
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 }
4.7、级联保存
可以设置级联保存,在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 }
4.8、级联删除
当设置了级联删除的时候,如果删除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 }
三. 一对一映射的两种设计方案
需求:公民表和身份证表是一对一的关系
设计表的两种方案:
1. 一对一唯一外键关联
Person
1 public class Person { 2 private Integer id; 3 private String name; 4 5 //关联身份证 6 private Card card; 7 }
Card
1 public class Card { 2 private Integer id; 3 private String cardno; 4 5 //关联公民 6 private Person person; 7 }
1.2 配置映射文件
<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>
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>
1.3 核心配置
1 <mapping resource="/pojo/one2one_fk/Person.hbm.xml"/> 2 <mapping resource="/pojo/one2one_fk/Card.hbm.xml"/>
1.4 测试