欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

精通Hibernate:映射一对多关联关系(1)

来源: javaer 分享于  点击 38955 次 点评:103

精通Hibernate:映射一对多关联关系(1)


在域模型中,类和类之间最普通的关系就是关联关系。在UML语言中,关联是有方向的。以客户Customer)和订单Order)的关系为例,一个客户可以发出多个订单,而一个订单只能属于一个客户。

从Order到Customer的关联是多对一关联,这意味着每个Order对象都会引用一个Customer对象,因此在Order类中应该定义一个Customer类型的属性,来引用所关联的Customer对象。

从Customer到Order的关联是一对多的关联,这意味着每个Customer对象都会引用一组Order对象,因此在Customer类中应该定义一个集合类型的属性,来引用所有关联的Order对象。

一、建立多对一的单向关联关系

如上例中,我们只需在Order类中定义一个customer属性,而在Customer类中无需定义存放Order对象的集合属性。

Order.java

  1. package mypack;  
  2.  
  3. public class Order  implements java.io.Serializable {  
  4.      private long id;  
  5.      private String orderNumber;  
  6.      private Customer customer;//定义一个Customer属性  
  7.  
  8.     public Order() {  
  9.     }  
  10.  
  11.     public Order(Customer customer) {  
  12.         this.customer = customer;  
  13.     }  
  14.     public Order(String orderNumber, Customer customer) {  
  15.        this.orderNumber = orderNumber;  
  16.        this.customer = customer;  
  17.     }  
  18.      
  19.    //省略了id,orderNumber的构造方法  
  20.     public Customer getCustomer() {  
  21.         return this.customer;  
  22.     }  
  23.       
  24.     public void setCustomer(Customer customer) {  
  25.         this.customer = customer;  
  26.     }  
  27.  

Customer类的所有属性都是和CUSTOMERS表中的字段一一对应,因此可以直接使用如下的映射代码:

  1. <class name="mypack.Customer" table="CUSTOMERS" >  
  2.    <id name="id" type="long" column="ID">  
  3.      <generator class="increment"/>  
  4.    </id>  
  5.  
  6.    <property name="name" type="string" >  
  7.        <column name="NAME" length="15" />  
  8.    </property>  
  9.        
  10.  </class

Order类的orderNumber属性和ORDERS表中ORDER_NUMBER字段对应,映射代码和上面类似,此处省去。我们关注的主要地方是,Order类中的customer属性,因为他是Customer类型的,是与ORDERS表的外键CUSTOMER_ID对应的,它的真实值是存在CUSTOMERS表中而ORDERS表存的只是对它的引用,因此customer的映射方法不能如上面一样。

  1. <many-to-one  
  2.         name="customer" 
  3.         column="CUSTOMER_ID" 
  4.         class="mypack.Customer" 
  5.         not-null="true"   
  6.         lazy="false" 
  7. /> 

使用方法のBussiness.java演示:

  1. package mypack;  
  2.  
  3. import org.hibernate.*;  
  4. import org.hibernate.cfg.Configuration;  
  5. import java.util.*;  
  6.  
  7. public class BusinessService{  
  8.   public static SessionFactory sessionFactory;  
  9.   static{  
  10.      try{  
  11.       // 初始化  
  12.        Configuration config = new Configuration();  
  13.        config.configure();  
  14.        sessionFactory = config.buildSessionFactory();  
  15.     }catch(RuntimeException e){e.printStackTrace();throw e;}  
  16.   }  
  17. /*根据参数指定customer的customer_id找出记录*/ 
  18.   public List findOrdersByCustomer(Customer customer){  
  19.     Session session = sessionFactory.openSession();  
  20.     Transaction tx = null;  
  21.     try {  
  22.       tx = session.beginTransaction();  
  23.  
  24.       List orders=session.createQuery("from Order as o where o.customer.id="+customer.getId())  
  25.                          .list();  
  26.       //Hibernate执行:select * from ORDERS where CUSTOMER_ID=customer.getId();  
  27.       tx.commit();  
  28.       return orders;  
  29.     }catch (RuntimeException e) {  
  30.       if (tx != null) {  
  31.         tx.rollback();  
  32.       }  
  33.       throw e;  
  34.     } finally {  
  35.       session.close();  
  36.     }  
  37.   }  
  38. /*根据OID找出指定customer_id的记录*/ 
  39.   public Customer findCustomer(long customer_id){  
  40.     Session session = sessionFactory.openSession();  
  41.     Transaction tx = null;  
  42.     try {  
  43.       tx = session.beginTransaction();  
  44.       Customer customer=(Customer)session.get(Customer.class,new Long(customer_id));  
  45.       tx.commit();  
  46.       return customer;  
  47.     }catch (RuntimeException e) {  
  48.       if (tx != null) {  
  49.         tx.rollback();  
  50.       }  
  51.       throw e;  
  52.     } finally {  
  53.       session.close();  
  54.     }  
  55.   }  
  56.  
  57. /*    
  58.     public void saveCustomerAndOrderWithCascade(){  
  59.     Session session = sessionFactory.openSession();  
  60.     Transaction tx = null;  
  61.     try {  
  62.       tx = session.beginTransaction();  
  63.  
  64.       Customer customer=new Customer("Jack");//创建一个Customer持久化对象  
  65.       //不保存customer对象,这样执行的话会出现异常  
  66.       Order order1=new Order("Jack_Order001",customer);  
  67.       Order order2=new Order("Jack_Order002",customer);//创建两个Order对象  
  68.  
  69.       session.save(order1);  
  70.       session.save(order2);  
  71.  
  72.       tx.commit();  
  73.  
  74.     }catch (RuntimeException e) {  
  75.       if (tx != null) {  
  76.         tx.rollback();  
  77.       }  
  78.       e.printStackTrace();  
  79.     } finally {  
  80.       session.close();  
  81.     }  
  82.   }  
  83.  
  84. */  public void saveCustomerAndOrder(){  
  85.     Session session = sessionFactory.openSession();  
  86.     Transaction tx = null;  
  87.     try {  
  88.       tx = session.beginTransaction();  
  89.  
  90.       Customer customer=new Customer("Tom");//创建一个Customer持久化对象  
  91.       session.save(customer);  
  92.  
  93.       Order order1=new Order("Tom_Order001",customer);  
  94.       Order order2=new Order("Tom_Order002",customer);//创建两个Order对象  
  95.       session.save(order1);  
  96.       session.save(order2);  
  97.      // 对同一个customerHibernate执行两次插入ORDERS表  
  98.       tx.commit();  
  99.  
  100.     }catch (RuntimeException e) {  
  101.       if (tx != null) {  
  102.         tx.rollback();  
  103.       }  
  104.       throw e;  
  105.     } finally {  
  106.       session.close();  
  107.     }  
  108.   }  
  109.     
  110.   public void printOrders(List orders){  
  111.       for (Iterator it = orders.iterator(); it.hasNext();) {  
  112.          Order order=(Order)it.next();  
  113.          System.out.println("OrderNumber of "+order.getCustomer().getName()+ " :"+order.getOrderNumber());  
  114.       }  
  115.   }  
  116.  
  117.    public void test(){  
  118.       saveCustomerAndOrder();  
  119.     //  saveCustomerAndOrderWithCascade();  
  120.       Customer customer=findCustomer(1);  
  121.       List orders=findOrdersByCustomer(customer);  
  122.       printOrders(orders);  
  123.   }  
  124.  
  125.   public static void main(String args[]){  
  126.     new BusinessService().test();  
  127.     sessionFactory.close();  
  128.   }  
  129. }  
  130. <span style="font-size:16px;color:#cc33cc;"><strong>  
  131. </strong></span> 

上述代码中方法 saveCustomerAndOrderWithCascade()如果没有session.save(customer)这一句,

执行时会抛出PropertyValueException异常,主要原因是:

在调用session.save(order1)方法之前,order1和customer对象都是临时的,临时对象是由new创建的,都是没有持久化的对象。假设 session.save(order1)被成功执行,order1会被成功持久化,变成持久化对象,但是Hibernate不会自动持久化order1所关联的customer对象。

在执行session.save(order1)时,插入ORDERS表记录的CUSTOMER_ID字段为null,这违反了数据库完整性约束,即ORDERS表中不允许CUSTOMER_ID为null。

疑问假设ORDERS表中CUSTOMER_ID字段允许为null:

  1. <many-to-one  
  2.         name="customer" 
  3.         column="CUSTOMER_ID" 
  4.         class="mypack.Customer" 
  5.         not-null="false"   
  6.         lazy="false" 
  7.      /> 

这样执行的话,能够成功的向ORDERS表中插入两条数据;但是当Hibernate自动清理flush)缓存中所有持久化对象时,又会抛出新的异常

  1. org.hibernate.TransientObjectException:object references an unsaved transient instance -save the transient instance before flushing :mypack.Customer 

所谓清理是指Hibernate按照持久化对象的属性变化来同步更新数据库。在清理的时候Hibernate会发现order1和order2都引用临时对象customer,而在ORDERS表中CUSTOMER_ID字段为null,这就意味着内存中持久化对象的属性和数据库中记录不一致。之所以会报错是因为order1中customer属性引用了一个临时对象Customer。

由此可见,Hibernate持久化一个对象时,默认情况下不会自动持久化所关联的其他对象。但是,我们我们希望当Hibernate持久化Order对象时自动持久化所关联的Customer对象,我们可以修改映射文件如下:

  1. <many-to-one  
  2.         name="customer" 
  3.         column="CUSTOMER_ID" 
  4.         class="mypack.Customer" 
  5.         cascade="save-update" 
  6.         not-null="false"   
  7.         lazy="false" 
  8.      /> 

当cascade属性为“save-update”,表明保存或更新对象时,会级联保存或更新与它所关联的对象。如上例中,执行saveCustomerAndOrderWithCascade()时,Hibernate会把order1与customer对象一起持久化,此时Hibernate会执行

  1. insert into CUSTOMERS(ID,NAME) values(2,"Jack");  
  2. insert into ORDERS(ID,ORDER_NUMBER,CUSTOMER_ID) value (3,"Jack_Order001",2); 


相关栏目:

用户点评