二、映射一对多双向关联关系
类类之间建立了联系,就可以很方便地从一个对象导航到另一个或者另一组与它相关联的对象。正如上例中,对于给定的Order对象,如果想获得与之关联的Customer对象,可以直接如下调用:
Customer customer=order.getCustomer();
那么对于给定的Customer对象,如何一次获得所有与之关联的Order对象呢?由于上例中Customer对象没有和Order对象关联,我们也可以通过Hibernate API去查询数据库:
List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId()).list
显然这样做的效率会很低,而且复杂的关联关系也会给编程带来影响。我们可以为Customer类和Order类建立一对多的双向关联。
第一部分我们已经建立了Order类和Customer类的多对一关联,现在我们再增加Customer到Order类的一对多关联。
Customer.java文件:
- package mypack;
- import java.util.HashSet;
- import java.util.Set;
- //Hibernate要求在持久化类中定义集合类属性时,必须要把属性声明为接口类型。
- publicclass Customer implements java.io.Serializable {
- privatelong id;
- private String name;
- private Set orders = new HashSet();//初始化为集合实现类,这样做可以提高程序的健壮性,同时避免了应用程序访问取词为null的orders集合的方法而抛出NullPointerException。
- public Customer() {
- }
- public Customer(String name, Set orders) {
- this.name = name;
- this.orders = orders;
- }
- //省略了id,name的get和set访问方法
- public Set getOrders() {
- returnthis.orders;
- }
- publicvoid setOrders(Set orders) {
- this.orders = orders;
- }
- }
接下来就是映射文件的配置Customer.hbm.xml:
- <class name="mypack.Customer" table="CUSTOMERS" >
- <id name="id" type="long" column="ID">
- <generator class="increment"/>
- </id>
- <property name="name" type="string" >
- <column name="NAME" length="15" />
- </property>
- <set
- name="orders" [1方Customer中,有多方orders,对应的键为CUSTOMER_ID,设置]
- cascade="save-update"
- <key column="CUSTOMER_ID" />//表示ORDERS表通过外键CUSTOMER_ID参照CUSTOMERS表
- <one-to-many class="mypack.Order" />
- </set>
- </class>
使用方法のBussiness.java演示分函数介绍:
(1)saveCustomerAndOrderWithCascade()方法:当映射文件中<set>的属性为“save-update”时,Hibernate在持久化Customer对象时也会自动持久化其所关联的Order对象
- publicvoid saveCustomerAndOrderWithCascade(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- /*创建一个customer对象和order对象*/
- Customer customer=new Customer("Tom",new HashSet());
- Order order=new Order();
- order.setOrderNumber("Tom_Order001");
- /*建立Customer与Order的一对多双向关联关系*/
- order.setCustomer(customer);
- customer.getOrders().add(order);
- /*保存Customer对象*/
- session.save(customer); /*保存1方的时候也会同时保存多方*/
- /* 当映射文件中<set>的属性为“save-update”时,Hibernate在持久化Customer对象时也会自动持久化其所关联的Order对象
- insert into CUSTOMERS(ID,NAME) values(1,"Tom");
- insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)*/
- tx.commit();
- idOfTom=customer.getId();
- idOfTomOrder=order.getId();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
当Customer映射文件中<set>的属性为“save-update”时,Hibernate在持久化Customer对象时也会自动持久化其所关联的Order对象
- insert into CUSTOMERS(ID,NAME) values(1,"Tom");
- insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(1,"Tom_Order001",1)
(2)printOrdersOfCustomer(Long customerId)方法:打印与指定customerId关联的所有Order对象
- publicvoid printOrdersOfCustomer(Long customerId){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- Customer customer=(Customer)session.get(Customer.class,customerId);
- printOrders(customer.getOrders());//使用getOrders获取一个order对象set
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- throw e;
- } finally {
- session.close();
- }
- }
其调用的函数printOrders(Set orders)
- publicvoid printOrders(Set orders){
- for (Iterator it = orders.iterator(); it.hasNext();) { [设计模式之迭代器模式]
- Order order=(Order)it.next();
- System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());
- }
- }
(3)saveCustomerAndOrderWithInverse()方法:演示映射文件<set>属性为inverse
- publicvoid saveCustomerAndOrderWithInverse(){
- saveCustomerAndOrderSeparately(); //先持久化Customer和Order两个对象
- associateCustomerAndOrder(); //再建立两者之间的关联
- }
调用的函数saveCustomerAndOrderSeparately():即是分别存储,与saveCustomerAndOrderWithCascade()方法恰好相反。
- Customer customer=new Customer();
- customer.setName("Jack");
- Order order=new Order();
- order.setOrderNumber("Jack_Order001");
- session.save(customer);
- session.save(order);
- tx.commit();
- idOfJack=customer.getId();
- idOfJackOrder=order.getId();
为了使上述代码正常执行,需要确保Order.hbm.xml文件的<many-to-one>元素的not null取默认值false,否则会出现异常;Hibernate会执行如下
- insert into CUSTOMERS(ID,NAME) values(2,"Jack");
- insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) values(2,"Jack_Order001",null);
调用的函数associateCustomerAndOrder():该方法加载由saveCustomerAndOrderSeparately()方法持久化Customer和Order对象,然后建立两者之间的一对多的关系
- publicvoid associateCustomerAndOrder(){
- Session session = sessionFactory.openSession();
- Transaction tx = null;
- try {
- tx = session.beginTransaction();
- /*加载持久化对象Customer、Order*/
- Customer customer=(Customer)session.load(Customer.class,idOfJack);
- Order order=(Order)session.load(Order.class,idOfJackOrder);
- /*建立Customer和Order的关联关系*/
- order.setCustomer(customer);
- customer.getOrders().add(order);
- tx.commit();
- }catch (RuntimeException e) {
- if (tx != null) {
- tx.rollback();
- }
- e.printStackTrace();
- } finally {
- session.close();
- }
- }
大家可以看到上述两个步骤中,虽然是让双方都知道有彼此的存在,但是,在1方Customer中存储多方Order时,执行的SQL语句,与多方Order中存储1方Customer的SQL语句一致。
这样重复执行多余的SQL语句会影响java应用的性能,解决的方法是将<set>的inverse属性设为true。因此修改Customer.hbm.xml文件:[1方XML中]
- <set
- name="orders"
- inverse="true"
- cascade="save-update"
- >
- <key column="CUSTOMER_ID" />//表示ORDERS表通过外键CUSTOMER_ID参照CUSTOMERS表
- <one-to-many class="mypack.Order" />
- </set>
(4)级联删除:
- tx = session.beginTransaction();
- Customer customer=(Customer)session.load(Customer.class,customerId);
- session.delete(customer);
- tx.commit();
如果要删除Customer所关联的Order对象的话,需要将cascade属性设置为delete,如下:
- <set
- name="orders"
- inverse="true"
- cascade="delete"
- >
- <key column="CUSTOMER_ID" />
- <one-to-many class="mypack.Order" />
- </set>
执行后,Hibernate会做以下动作:
- delete from ORDERS where CUSTOMER_ID=2; //再删除1方的时候,先删除多方,再删除1方
- delete from CUSTOMERS where ID=2;
如果关联双方是父子关系,就可以把父方的cascade设置为all-delete-orphan;这样删除父方对象时就会级联删除所有关联的子方对象。