hibernate 中inverse cascade属性转.docx
《hibernate 中inverse cascade属性转.docx》由会员分享,可在线阅读,更多相关《hibernate 中inverse cascade属性转.docx(8页珍藏版)》请在冰豆网上搜索。
hibernate中inversecascade属性转
04
hibernate中inversecascade属性(转)
在hibernate中一对多关联时会经常用到inverse和cascade属性,
inverse有两个值true,false ;如果设置为true则表示对象的状态变化不会同步到数据库;设置false就相反拉;
cascade有五个选项分别是:
all,delete,none,save-update,delete-orphan;
all:
所有情况下均进行关联操作。
none:
所有情况下均不进行关联操作。
这是默认值。
save-update:
在执行save/update/saveOrUpdate时进行关联操作。
delete:
在执行delete时进行关联操作。
delete-orphan:
当save/update/saveOrUpdate时,相当于save-update;当删除操作时,相当于delete;
all的意思是save-update+delete
all-delete-orphan的意思是当对象图中产生孤儿节点时,在数据库中删除该节点
all比较好理解,举个例子说一下all-delete-orphan:
Category与Item是一对多的关系,也就是说Category类中有个Set类型的变量items.
举个例子,现items中存两个Item,item1,item2,如果定义关系为all-delete-orphan
当items中删除掉一个item(比如用remove()方法删除item1),那么被删除的Item类实例
将变成孤儿节点,当执行category.update(),或session.flush()时
hibernate同步缓存和数据库,会把数据库中item1对应的记录删掉
测试Hibernate中的三个属性:
lazy,inverse,cascade
【测试环境】
一对多关系的两张表:
boy、girl(一个男孩可以多个女朋友)
boy表结构
Field Type
------ -----------
name varchar(50) pk
age varchar(50)
girl表结构
Field Type
------ -----------
name varchar(50) pk
bf varchar(50) fk
ER图
【保存时:
Inverse与cascade】
创建三个girl对象和一个boy对象,让这是三个girl都是boy的女朋友
---------创建对象的代码片段-----------
Boyboy=newBoy("tom","23",null);
Setgirls=newHashSet();
Girlg[]=newGirl[]{
newGirl("Alice1",boy),
newGirl("Alice2",boy),
newGirl("Alice3",boy)};
girls.add(g[0]);
girls.add(g[1]);
girls.add(g[2]);
boy.setGirls(girls);
在Boy.hbm.xml中设置,然后对boy对象进行保存。
1.Inverse=true,不指定cascade
cascade的默认值为none,当对boy进行保存操作时,girl什么都不做.所以只保存了boy对象,没有保存girl对象
2.Inverse=true,cascade=all
boy与girl对象,包扩外键都成功保存。
(生成3条SELECT语句和4条INSERT语句,一下简称SELECT3,INSERT4)
3.Inverse=false,不指定cascade
报错。
因为boy为主控方,负责维护关系,所以在插入boy对象后,会尝试修改并不存在的girl对象。
4.Inverse=false,cascade=all
boy与girl对象,包扩外键都成功保存。
(SELECT4,INSERT4,UPDATE3)
分析:
除了4条INSERT语句之外,其他的6条语句是我们为了图方便付出的代价:
3条SELECT语句用来判断girl对象是否在数据表中已经存在,3条UPDATE语句是为了维护外键关系
高效率的做法:
在Boy.hbm.xml中设置Inverse=true,在Girl.hbm.xml中设置Inverse=false,cascade=all,然后保存三个girl对象
(SELECT1,INSERT4)
高效率的代价就是保存的时候比较麻烦
【删除时:
Inverse与cascade】
希望通过删除boy,也将3个girl对象删除。
程序中先查出boy对象,然后进行删除
-----------------------------------------
Boyboy=(Boy)s.get(Boy.class,"tom");
s.delete(boy);
-----------------------------------------
同样在Boy.hbm.xml中进行设置
1.Inverse=true
可以猜到结果是出错。
原因:
外键约束错误
2.Inverse=false
boy删除,girl表中外键变为null,没有删除记录;
(UPDATE1,DELETE1)
3.Inverse=false,cascade=all
全部删除 ;在删除有外键的从表时,先把从表外键置为null,然后删除主表记录,最后根据从表主键删除所有相关从表记录
(UPDATE1,DELETE4)
4.Inverse=true,cascade=all
全部删除
(DELETE4)
Inverse是hibernate双向关系中的基本概念,当然对于多数实体,我们并不需要双向关联,更多的可能会选择单向关联,况且我们大多数人一般采用一对多关系,而一对多双向关联的另一端:
多对一的inverse属性是不存在,其实它默认就是inverse=false.从而防止了在一对多端胡乱设置inverse也不至于出错。
但是inverse设置不当确实会带来很大的性能影响,这点是我们必须关注的。
这篇文章已经详细分析了inverse设置不当带来的影响:
http:
//www.hibernate.org/155.html
看了这篇文章,还是很有必要再写下一些总结的:
1)inverse中提及的side其实是指一个类或者表的概念,双向关联其实是指双方都可以取得对方的应用。
2)维护关系这个名词还是稍显模糊或者晦涩。
我们一般说A类或者A表(这里的表的是指多对多的连接表)有责任维护关系,其实这里的意思是说,我在应用在更新,创建,删除(读就不用说了,双向引用正是为了方便读而出现)A类或者A表时,此时创建的SQL语句必须有责任保证关系的正确修改。
3)inverse=false的side(side其实是指inverse=false所位于的class元素)端有责任维护关系,而inverse=true端无须维护这些关系。
4)我们说inverse设立不当会导致性能低下,其实是说inverse设立不当,会产生多余重复的SQL语句甚至致使JDBCexception的throw。
这是我们在建立实体类关系时必须需要关注的地方。
一般来说,inverse=true是推荐使用,双向关联中双方都设置inverse=false的话,必会导致双方都重复更新同一个关系。
但是如果双方都设立inverse=true的话,双方都不维护关系的更新,这也是不行的,好在一对多中的一端:
many-to-one默认是inverse=false,避免了这种错误的产生。
但是对多对就没有这个默认设置了,所以很多人经常在多对多的两端都使用inverse=true,结果导致连接表的数据根本没有记录,就是因为他们双分都没有责任维护关系。
所以说,双向关联中最好的设置是一端为inverse=true,一端为inverse=false。
一般inverse=false会放在多的一端,那么有人提问了,many-to-many两边都是多的,inverse到底放在哪儿?
其实hibernate建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。
所以通用存在一对多的关系,也可以这样说:
一对多是多对多的基本组成部分。
看下面的多对多的定义大家更会清楚”多对多“与“一对多”的关系:
其中我们注意标签的特点就知道,它是定义了一个多对多关系,而不是。
xmlversion="1.0"?
>
DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD2.0//EN"
"
dynamic-update="true"dynamic-insert="true">
update="true"insert="true"column="name"/>
dynamic-update="true"dynamic-insert="true">
insert="true"column="name"/>
在对多对中,因为一端维护关系另一端不维护关系的原因,我们必须注意避免在应用中用不维护关系的类建立关系,因为这样建立的关系是不会在数据库中存储的。
基于上面的映射文件代码给出一个例子:
packageorg.hibernate.auction;
importjava.util.*;
/**
*@authorAdministrator
*
*Tochangethetemplateforthisgeneratedtypecommentgoto
*Window>Preferences>Java>CodeGeneration>CodeandComments
*/
publicclassTestA{
intid;
Stringname;
SettestBs=newHashSet();
publicTestA(){
}
publicTestA(intid){
setId(id);
}
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicSetgetTestBs(){
returntestBs;
}
publicvoidsetTestBs(Sets){
testBs=s;
}
publicvoidaddTestB(TestBtb){
testBs.add(tb);
}
publicstaticvoidmain(String[]args){
}
}
publicclassTestB{
intid;
Stringname;
SettestAs=newHashSet();
publicTestB(){
}
publicTestB(intid){
setId(id);
}
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicSetgetTestAs(){
returntestAs;
}
publicvoidsetTestAs(Sets){
testAs=s;
}
publicvoidaddTestA(TestAta){
testAs.add(ta);
}
publicstaticvoidmain(String[]args){
}
}
测试代码:
publicvoiddoTest()throwsException{
TestAa1=newTestA
(1);
TestAa2=newTestA
(2);
TestAa3=newTestA(3);
TestBb1=newTestB
(1);
TestBb2=newTestB
(2);
TestBb3=newTestB(3);
a1.addTestB(b1);
a1.addTestB(b2);
a1.addTestB(b3);
b2.addTestA(a1);
b2.addTestA(a2);
Sessions=factory.openSession();
s=factory.openSession();
Sessionsession=factory.openSession();
session.save(a1);
session.flush();
session.close();
}
测试后连接表的数据为:
testa testb
1 1
1 2
1 3
根据inverse规则,对这些代码:
b2.addTestA(a1); b2.addTestA(a2);建立的关系,数据库并没有存储下来,因为TestB没有责任维护这些关系,所以产生的sql语句自然不会有针对Testa_testB表的操作了。
假设应用中真的需要这些方法,那么我们可以修改TestB的方法,让他们注意在维护端类中执行相应的操作以使得关系能够在数据库中保存下来,更改TestB如下:
/*
*Createdon2004-7-25
*
*Tochangethetemplateforthisgeneratedfilegoto
*Window>Preferences>Java>CodeGeneration>CodeandComments
*/
packageorg.hibernate.auction;
importjava.util.*;
/**
*@authorAdministrator
*
*Tochangethetemplateforthisgeneratedtypecommentgoto
*Window>Preferences>Java>CodeGeneration>CodeandComments
*/
publicclassTestB{
intid;
Stringname;
SettestAs=newHashSet();
publicTestB(){
}
publicTestB(intid){
setId(id);
}
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicSetgetTestAs(){
returntestAs;
}
publicvoidsetTestAs(Sets){
testAs=s;
}
publicvoidaddTestA(TestAta){
testAs.add(ta);
ta.addTestB(this);
}
publicstaticvoidmain(String[]args){
}
}
那么测试执行后连接表的数据为:
testa testb
1 2
1 3
1 1
2 2