既然我们讲到了一对多和多对一关系,必然要提到多表设计的问题。在开发中,前期需要进行需求分析,希求分析提供E-R图,根据ER图编写表结构。
我们知道表之间关系存在三种:
一对多&多对一:1表(主表)必须主键 和 多表(从表)必须外键,主表的主键 与 从表外键 形成主外键关系
多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表。
一对一
下面演示的是一对多&多对一的关系,和上面写的那样使用Customer和Order之间的关系来举例子。
一、实现类
1.Customer.java
2.Order.java
二、配置文件
<?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 package="com.jxlg.domain"> <class name="Customer" table="t_customer"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name" type="string"></property> <!-- 一对多:一个客户(当前客户) 拥有 【多个订单】 表达一对多关系中的集合 name:集合的属性名称 inserse:是否将关系的维护反转给对方。默认值:false true:在Customer中放弃维护外键关系。 --> <set name="orders" inverse="true"> <!-- key:用来描述外键 column:外键的值 --> <key column="cid"></key> <!--one-to-many 表达,Customer与orders的关系是一对多 class:表达关联另一方的完整类名 --> <one-to-many class="Order"/> </set> </class> </hibernate-mapping>
<?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 package="com.jxlg.domain"> <class name="Order" table="t_order"> <id name="id" column="id"> <generator class="native"></generator> </id> <property name="name" column="name" type="string"></property> <!-- 多对一:多个订单属于【一个客户】 表达多对一的关系 name:引用的属性名称 column:外键的列名 class:我引用的Order的完整类名 --> <many-to-one name="customer" column="cid" class="Customer"></many-to-one> </class> </hibernate-mapping>
三、一对多的操作
3.1、一对多关系的基本操作
package com.jxlg.onetomany; import static org.junit.Assert.*; import java.util.Set; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.jxlg.domain.Customer; import com.jxlg.domain.Order; import com.jxlg.util.HibernateUtils; //测试一对多关系 public class Demo1 { //一对多关系的保存操作 //共打印5条语句 //前三条insert=》用来保存对象,维护外键 //后两条update =》用来维护外键 //解决:单纯的指定 关系由一方来维护,另一方不维护关系。 //注意:外键维护的放弃,只能由非外键所在对象放弃。 //Customer inverse=true //只打印三天语句 =》外键由order自己来维护 @Test public void test1() { Session session = HibernateUtils.openSession(); Transaction ts = session.beginTransaction(); //---------------------------------------- Customer cus = new Customer(); cus.setName("jerry"); Order or1 = new Order(); or1.setName("酱油"); Order or2 = new Order(); or2.setName("练习本"); //cus.getOrders().add(or1);//维护关系 //cus.getOrders().add(or2);//维护关系 or1.setCustomer(cus);//维护关系 or2.setCustomer(cus);//维护关系 session.save(cus); session.save(or1); session.save(or2); //---------------------------------------- ts.commit(); session.close(); } //多表关系 =》删除 //删除 用户时,会先移除Customer中引用的外键,然后再删除Customer //结论:维护一方的对象时,会自动维护另一方的关系。 //Customer 的inverse属性:true //会报错=》Customer不负责维护外键,直接删除Customer会导致,Order引用了无效的id,违反了外键约束。 @Test public void test2() { Session session = HibernateUtils.openSession(); Transaction ts = session.beginTransaction(); //---------------------------------------- Customer c = (Customer) session.get(Customer.class, 5); //解决 :Customer 的inverse属性:true Set<Order> set = c.getOrders(); for(Order o :set){ o.setCustomer(null);//设置订单不属于任何Customer } session.delete(c); //---------------------------------------- ts.commit(); session.close(); } //什么时候配置inverse属性? //主要看业务,如果一的一方经常需要维护外键 = 在1的一方不要配置inverse属性。 }
3.2、级联操作
在fun1中
测试一:cascade的值为:save-update、delete
/测试 一对多关系 public class Demo2 { @Test //增 //我们希望在保存Customer时,自动将未保存的Orders当中的Order保存 //cascade: save-update public void fun1(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = new Customer(); c.setName("tom"); Order o1 = new Order(); o1.setName("肥皂"); Order o2 = new Order(); o2.setName("蜡烛"); c.getOrders().add(o1);//维护关系 c.getOrders().add(o2); //维护关系 /* o1.setCustomer(c);//维护关系 o2.setCustomer(c);//维护关系 */ session.save(c);//保存对象 //session.save(o1);//保存对象 //session.save(o2);//保存对象 //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } @Test //增 //我们希望在保存Customer时,自动将未保存的Orders当中的Order保存 //cascade: save-update public void fun2(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = (Customer) session.get(Customer.class, 8);//1条 select for(Order o :c.getOrders()){ // 1条 select o.setName("哇哈哈"); // 修改订单 } //------------------------------------------------ session.getTransaction().commit();//因为设置级联修改,自动将订单的修改保存到数据 //update语句 session.close(); // 游离状态 } @Test //cascade: delete //删除Customer时 ,会将Customer下的订单一并删除 //inverse : false 6条sql语句 //inverse : true 5条sql语句 比上面少一条维护外键 public void fun3(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = (Customer) session.get(Customer.class, 7);//1条 select session.delete(c);//删除Customer // 删除两个Order //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } @Test //cascade: delete //操作的两方cascade值都为delete //需要注意: 千万不要在两方都配置 级联删除. 删除任何一方,会导致整个关系链对象全部删除. public void fun4(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Order o = (Order) session.get(Order.class, 9);//select session.delete(o);//delete删除当前order //找到所有关联的Customer删除 select // delete Customer // Customer配置了级联删除=> select 找下面的order // 删除所有Order //删除Customer //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } }
测试二:cascade的值为:delete-orphan
public class Demo3 { @Test //inverse:false //cascade: delete-orphan 孤儿删除 => 当没有任何外键引用Order时,order 会被删除 public void fun1(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = (Customer) session.get(Customer.class, 9); Iterator<Order> it = c.getOrders().iterator(); //注意: 删除Customer下的订单时,不能使用 c.setOrders(null); c.setOrders(new HashSet()); while(it.hasNext()){ // 遍历Customer下的订单,并将订单删除 => 维护关系 it.next(); it.remove(); } //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } }
测试三: cascade的值为:all-delete-orphan
/测试 一对多关系 public class Demo4 { @Test //cascade: all-delete-orphan => 相当于配置 save-update,delete,delete-orphan public void fun1(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = new Customer(); c.setName("tom"); Order o1 = new Order(); o1.setName("肥皂"); Order o2 = new Order(); o2.setName("蜡烛"); c.getOrders().add(o1);//维护关系 c.getOrders().add(o2); //维护关系 session.save(c); //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } @Test //cascade: all-delete-orphan => 相当于配置 save-update,delete,delete-orphan public void fun2(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = (Customer) session.get(Customer.class, 10); session.delete(c); //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } @Test //cascade: all-delete-orphan => 相当于配置 save-update,delete,delete-orphan public void fun3(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); //------------------------------------------------ Customer c = (Customer) session.get(Customer.class, 12); Iterator<Order> it = c.getOrders().iterator(); while(it.hasNext()){ // 遍历Customer下的订单,并将订单删除 => 维护关系 it.next(); it.remove(); } //------------------------------------------------ session.getTransaction().commit(); session.close(); // 游离状态 } }
联级操作总结:
save-update:A保存,同时保存B
delete:删除A,同时删除B,AB都不存在
delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">
all : save-update 和 delete 整合
all-delete-orphan : 三个整合