精通Hibernate类与类关联关系:[一]建立多对一的单向关联关系

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

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

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

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

      如上例中,我们只需在Order类中定义一个customer属性,而在Customer类中无需定义存放Order对象的集合属性。[Order 表中有Customer_ID,即Order知道Customer表的信息]

      Order.java

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

      Customer类的所有属性都是和CUSTOMERS表中的字段一一对应,因此可以直接使用如下的映射代码:[Customer 中没有Order属性信息,即,不知道Order的存在]

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

  • 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对象,我们可以修改映射文件如下:

  • <many-to-one                   [Ordrer     多方中的映射文件]
  •         name="customer"
  • column="CUSTOMER_ID"
  •         class="mypack.Customer"
  •         cascade="save-update"
  •         not-null="false"  
  •         lazy="false"
  •      />

     当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);
原文地址:https://www.cnblogs.com/wukong0214/p/2872788.html