如果要了解多对多的映射,首先需要学习级联设置和关系权反转
一、级联保存
1、测试级联保存
测试:复制前面的例子,只保存Customer数据,不保存Order数据,会报告错误
/** * 保存操作 - 级联保存 */ @Test public void testCascadeSave(){ //准备数据 ... //建立一对多双向关系 ... //保存数据 session.save(customer); //session.save(o1); //session.save(o2); //数据提交 ... } |
2、配置级联保存
级联操作:
就是操作一个对象的时候,想同时操作它的关联对象。
在Customer的一对多中配置级联保存,再次进行测试,保存成功
<set name="orders" cascade="save-update"> |
配置了级联保存后,这里可以只设置Customer对order的单项关联
//建立一对多单向关系 customer.getOrders().add(o1); customer.getOrders().add(o2); //o1.setCustomer(customer); //o2.setCustomer(customer); |
二、级联删除
1、测试级联删除
(1)如果没有级联删除,那么在删除客户的时候,会把订单表的customer_id外键值设置为null ,
(2)也可以事先把订单列表取出来,业务实现上先删除订单,然后再删除客户
/** * 级联删除 */ @Test public void testCascadeDelete(){ Session session = HibernateUtil.openSession(); Transaction tx = session.beginTransaction(); Customer customer = session.get(Customer.class, 1L); /* Set<Order> orders = customer.getOrders(); for (Order order : orders) { session.delete(order); }*/ session.delete(customer); tx.commit(); session.close(); } } |
2、配置级联删除
有了级联删除,那么只删除客户,hibernate会同时把该客户的所有订单删除
<set name="orders" cascade="save-update,delete"> |
三、同时设置级联保存和删除
<set name="orders" cascade="all"> |
四、什么叫关系权反转
inverse 配置:表示是否把关联关系的维护权反转(放弃)
false:默认值,不反转(不放弃)
true:反转(放弃)
五、关系反转
1、测试关系反转
运行级联保存的测试用例,
查看日志中的sql语句
插入一个用户、两个订单,应该执行3个insert语句
但是发现日志中多打印了两个update语句
默认情况下inverse的值是false:
<set name="orders" cascade="all" inverse="false"> |
表示customer 一方需要维护关联关系,因此需要维护外键,有关联记录生成时,会做外键的更新操作。
而这个更新操作是没有必要的,
因为order插入的时候已经将外键值插入。
所以customer中的update的语句是多余的
2、优化
放弃customer方的外键维护
<set name="orders" cascade="all" inverse="true"> |
注意:使用关系反转时要将双向关系都建立起来
//建立双向关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);
order1.setCustomer(customer);
order2.setCustomer(customer); |
重新测试,发现只有三条insert语句
补充:通常在一对多的关联配置中,多方无法放弃关系维护权,所以应该放弃 1
方的维护权,意味着在 1 方加上 inverse=true 配置
2、第二种优化方式:不使用关系反转
(1)不设置前面的关系反转
(2)保存时,只保存订单,只在订单方维护关系
//建立一对多双/单向关系
//customer.getOrders().add(o1); //customer.getOrders().add(o2); o1.setCustomer(customer); o2.setCustomer(customer); //保存数据 //session.save(customer); session.save(o1); session.save(o2); |
(3)在订单端设置级联保存
<!-- 多对一配置 -->
<many-to-one name="customer" class="Customer" column="customer_id" cascade="save-update"/> |
但是,这种方式在业务逻辑上不通顺,应该先有客户后有订单
因此,最好的优化方式是在“一方”设置“级联保存”