Hibernate的inverse和cascade详解

Hibernate的inverse和cascade详解

cascade为级联操作,可以设置以下属性:

1、none:忽略其他关联的对象,默认值。 2、save-update:当session通过save(),update(),saveOrUpdate()方法来保存或更新对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离对象。 3、persist:当session通过persist()方法来保存当前对象时,会级联保存所有关联的新建的临时对象。 4、merge:通过Session的merge()方法来保存当前对象时,会级联融合所有关联的游离对象。 5、delete:通过delete()删除当前对象时,会级联删除所有关联的对象。 6、lock:通过lock()把当前游离对象加入session缓存时,会把所有的游离对象也加入Session缓存中。 7、replicate:通过replicate()复制当前对象时,会级联复制所有关联的对象。 8、evict:通过evict()清除session缓存中对象时,会级联清除所有关联的对象。 9、refresh:通过refresh()刷新当前对象时,会级联刷新所有关联的对象。(刷新是指同步更新session缓存中数据) 10、all:save-update(),persist(),merge(),delete(),lock(),replicate(),evict()及refresh()的行为。 11、delete-orphan,删除所有和当前对象时,解除关联行为的对象。

12、all-delete-orphan; 通过delete()删除当前对象时,会级联删除所有关联的对象

inverse(控制方向反转):

关系(Relationship)由两方组成。inverse默认值为false,此属性只存在于set类集合的配置中,作用是设置表与表之间的关系(即存在域另一个表中的外键)维护由哪一方来维护。其默认状态(即inverse=false)表示不启用,表示由己方(即当前含set类集合可配置inverse的一方)来维护。若inverse=true,则将维护权交给另一方(即配置inverse=true的另一方)来维护。

实例解述:

Customer类:

public class Customer { 
	PRivate int id; 
	private String name;
<span style="white-space:pre">	</span>private Set orders = new HashSet();
<span style="white-space:pre">	</span>//...此处省略
}
即Customer类具有一个set集合属性orders,其中Order是一个普通的类:
public class Order { 
	private int id; 
	private String orderName;
<span style="white-space:pre">	</span><span style="font-family: Arial, Helvetica, sans-serif;">//...此处省略</span><span style="white-space:pre">
</span>}
数据库中表的结构:
t_customer:  两个字段:id  name
t_order:     三个字段:id  orderName  customerid
Customer类的映射文件:Customer.hbm.xml (Order类的映射文件忽略) 
<hibernate-mapping>
<span style="white-space:pre">	</span><class name="test.Customer" table="t_customer" lazy="false">  
		<id name="id"> 
		   <generator class="native"/>
		</id>  
		<property name="name"/>  
		<set name="orders"  cascade="save-update"  lazy="false">
		   <key column="customerid"/>
		   <one-to-many class="test.Order"/>
		</set>
    <span style="white-space:pre">	</span></class> 
</hibernate-mapping>
执行如下代码:
Set orders = new HashSet(); 
			
Order o1 = new Order();
o1.setOrderName("o1"); 
Order o2 = new Order();
o2.setOrderName("o2");	 
orders.add(o1);
orders.add(o2);  	
			
Customer c = new Customer();
c.setName("aaa");
c.setOrders(orders);  

session.save(c); 
此时Hibernate发出的sql语句如下:
Hibernate: insert into t_customer (name) values (?)
Hibernate: insert into t_order (orderName) values (?)
Hibernate: insert into t_order (orderName) values (?)
Hibernate: update t_order set customerid=? where id=?
Hibernate: update t_order set customerid=? where id=?
查看数据库:
t_customer :                    t_order:   

id   |  name                   id   |   orderName   |   customerid 
1       aaa                    1           o1              1
                               2           o2              1 
保存Customer对象时,首先发出insert
 into t_customer (name) values (?)语句将c同步到数据库,由于在<set>映射中设置cascade="save-update",所以会同时保存orders集合中的Order类型的o1,o2对象(如果没有这个设置,即cascade="save-update"),那么Hibenrate不会自动保存orders集合中的对象,那么在更新时将会抛出如下异常:
Hibernate: insert into t_customer (name) values (?)
Hibernate: update t_order set customerid=? where id=?
org.hibernate.TransientObjectException: test.Order
...
抛出这一异常的原因是:<set>映射默认"inverse=fasle"即由Customer对象作为主控方,那么它要负责关联的维护工作,在这里也就是负责更新t_order表中的customerid字段的值,但由于未设置cascade="save-update",所以orders集合中的对象不会在保存customer时自动保存,因此会抛出异常(如果未设置,需要手动保存)。
现在设置cascade="save-update",同时设置inverse="true",即: 
<!-- 此处有省略 ... -->
<set name="orders" cascade="save-update" inverse="true" lazy="false">
	<key column="customerid"/>
	<one-to-many class="test.Order"/>
</set>  
<pre name="code" class="java"><!-- 此处有省略 ... -->

同样执行上述代码,Hibernate发出如下语句:
Hibernate: insert into t_customer (name) values (?)
Hibernate: insert into t_order (orderName) values (?)
Hibernate: insert into t_order (orderName) values (?)
相比上一次执行,少了两条update语句,查看数据库:
t_customer :                    t_order:   

id   |  name                   id   |   orderName   |   customerid 
1       aaa                    1           o1              NULL
                               2           o2              NULL
发现t_order表中customerid的值为NULL,这是由于设置了inverse="true",它意味着 Customer不再作为主控方,而将关联关系的维护工作交给关联对象Orders来完成。在保存Customer时,Customer不在关心Orders的customerid属性,必须由Order自己去维护,即设置order.setCustomer(customer);

如果需要通过Order来维护关联关系,那么这个关联关系转换成双向关联。 
修改Order类代码:
public class Order { 
	private int id; 
	private String orderName;  
	private Customer customer;
<span style="white-space:pre">	</span>//...此处省略
}
Order.hbm.xml:
<hibernate-mapping>
	<class name="test.Order" table="t_order">  
		<id name="id"> 
		   <generator class="native"/>
		</id>  
		<property name="orderName"/>   
		<many-to-one name="customer" column="customerid"/> 
    </class> 
</hibernate-mapping>
再次执行上述代码,Hibernate发出如下sql语句:
Hibernate: insert into t_customer (name) values (?)
Hibernate: insert into t_order (orderName, customerid) values (?, ?)
Hibernate: insert into t_order (orderName, customerid) values (?, ?) 发现在保存Order对象时为customerid字段赋值,因为Order对象中拥有Customer属性,对应customerid字段,查看数据库表:
t_customer :                    t_order:   

id   |  name                   id   |   orderName   |   customerid 
1       aaa                    1           o1              NULL
                               2           o2              NULL
发现customerid的值仍为NULL,因为在上述代码中并未设置Order对象的Customer属性值,由于设置了inverse="true",所以Order对象需要维护关联关系,所以必须进行置,即 order.setCustomer(customer);

修改上述添加数据代码为:
//...此处省略
Customer c = new Customer();
			
Set orders = new HashSet(); 
Order o1 = new Order();
o1.setOrderName("o1"); 
o1.setCustomer(c);
Order o2 = new Order();
o2.setOrderName("o2");
o2.setCustomer(c);
orders.add(o1);
orders.add(o2);  	
			
c.setName("aaa");
c.setOrders(orders); 
			
session.save(c); 
//...此处省略
执行上述代码,发出如下语句:
Hibernate: insert into t_customer (name) values (?)
Hibernate: insert into t_order (orderName, customerid) values (?, ?)
Hibernate: insert into t_order (orderName, customerid) values (?, ?)
查看数据库:
t_customer :                    t_order:   

id   |  name                   id   |   orderName   |   customerid 
1       aaa                    1           o1              1
                               2           o2              1
发现已经设置了customerid的值。

在一对多关联中,在多的一方设置inverse="true",有助于性能的改善。通过上述分析可以发现少了update语句。

PS:在一对多的关联关系中,合理的设置cascade 属性和inverse属性,可以达到数据库优化的效果,大大提高了程序的性能。