|||
在域模型中,类与类之间最普遍的关系莫过于关联关系。在UML语言中,关联是有方向的,包括单向关系和双向关系。以客户(Customer)和订单(Order)的关系为例,一个客户能发出多个订单,而一个订单只能属于一个客户。
1. 建立多对一的单向关联关系
本节以订单与客户这种多对一的单向关联关系为例进行说明。
(1)为建立起多对一的关联关系,Order类需声明对Customer的私有变量以及其访问该变量的setter和getter方法。
private Customer customer;
(2)在映射文件中通过<many-to-one>建立customer属性和数据库表orders的外键CUSTOMER_ID之间的映射。
<many-to-one name="customer"
column="CUSTOMER_ID"
class="org.test.pojo.Customers"
not-null="true"
lazy="false"
/>
几点说明:映射文件中的not-null属性默认值为false,若为true则表示customer属性不为null,Hibernate在运行时保存Order对象时会先检查customer属性是否为null;lazy若为false,则Hibernate从数据库中装载Order对象时会自动加载与它关联的Customer对象。
Transaction trasaction = session.beginTransaction();
Customers c = new Customers();
c.setName("chengdongOrder");
Orders order = new Orders();
order.setCustomer(c);
order.setOrderNumber("16");
order.setPrice(10.0);
session.save(order);
trasaction.commit();// 自动清理缓存,执行insert操作,提交事务
运行报错:org.hibernate.PropertyValueException: not-null property references a null or transient value:
org.test.pojo.Orders.customer
Hibernate不会自动持久化对象order关联的临时对象c,在数据库中意味着向orders表中新增一条记录并且这条记录的外键CUSTOMER_ID为空,这显然违反了非空约束。因此该异常的根本原因在于order对象的非空属性引用了一个临时变量c。若把not-null="true"去掉,则会报错:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: org.test.pojo.Customers
这也是由于表orders的外键CUSTOMER_ID不允许为null造成的。解决办法要么在session.save(order)前持久化对象c,例如:
Transaction trasaction = session.beginTransaction();
Customers c = new Customers();
c.setName("chengdongOrder");
Orders order = new Orders();
session.save(c);// 持久化对象c,为该对象分配唯一的OID并加入到session缓存
order.setCustomer(c);
order.setOrderNumber("17");
order.setPrice(10.0);
session.save(order);
trasaction.commit();// 自动清理缓存,执行insert操作,提交事务
要么级联保存或更新持久化对象c,cascade="save-update"表明保存或更新当前对象时(即执行update语句时),会级联保存或更新与它关联的对象。
<many-to-one name="customer"
column="CUSTOMER_ID"
class="org.test.pojo.Customers"
cascade="save-update"
not-null="true"
lazy="false"
/>
根据业务需求,有些时候需要将customer与order建立起一对多双向关联关系。
2. 一对多双向关联关系
order与customer之间的多对一关联关系已建立,下面将建立customer与order之间的一对多的关系。
(1) Customers类中需声明接口类型的集合属性以及它的setter和getter方法。
/**
* Hibernate要求在持久化类声明集合属性时,必须为接口类型.
* java.util.Set|java.util.Map|java.util.List
*/
private Set orders = new HashSet();
(2) 在映射文件中通过<set>映射集合类型的orders属性。
<set name="orders" cascade="save-update">
<!-- key表明ORDERS表通过外键CUSTOMER_ID引用customers表 -->
<key column="CUSTOMER_ID" ></key>
<!-- 表明orders集合存放的是一组org.test.pojo.Orders类对象 -->
<one-to-many class="org.test.pojo.Orders"/>
</set>
几点说明:
Transaction trasaction = session.beginTransaction();
Customers c = new Customers();
c.setName("chengdong1");
Orders order = new Orders();
order.setOrderNumber("1");
order.setPrice(1.0);
// 建立Customers对象与Orders对象一对多双向关联关系
order.setCustomer(c);
c.getOrders().add(order);
// 持久化customer对象的同时持久化与其关联的一些对象
session.save(c);
// 清理缓存
trasaction.commit();
清理缓存时打印出的SQL语句:
Hibernate: insert into customers (name, ID) values (?, ?)
Hibernate: insert into orders (ORDER_NUMBER, PRICE, CUSTOMER_ID, ID) values (?, ?, ?, ?)
Hibernate: update orders set CUSTOMER_ID=? where ID=?
trasaction.commit()时,Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的属性变化同步更新到数据库。尽管只是向数据库表中插入数据,但还是执行了update语句。重复执行多余的SQL语句显然会影响Java应用的性能。解决办法是<set>设置inverse="true",即,
<set name="orders" cascade="save-update" inverse="true">
<!-- key表明ORDERS表通过外键CUSTOMER_ID引用customers表 -->
<key column="CUSTOMER_ID" ></key>
<!-- 表明orders集合存放的是一组org.test.pojo.Orders类对象 -->
<one-to-many class="org.test.pojo.Orders"/>
</set>
这样在建立customer与order之间的双向关联关系时,当Hibernate监测到持久化对象customer和order的属性均发生变化,仅按照order对象属性的变化同步更新数据库。设置inverse="true"后打印的SQL语句:
Hibernate: insert into customers (name, ID) values (?, ?)
Hibernate: insert into orders (ORDER_NUMBER, PRICE, CUSTOMER_ID, ID) values (?, ?, ?, ?)
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-5-19 00:16
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社