hibernate invese理解(转存)
在hibernate中是通过inverse的设置来决定是有谁来维护表和表之间的关系的
最近有朋友问我hibernate关于多对多关于删除中间表数据的问题,关键是inverse的设置.下面引用网友的一篇文章
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建立多对多关系也是将他们分离成两个一对多关系,中间连接一个连接表。所以通用存在一对多的关系,也可以这样说:一对多是多对多的基本组成部分。
看下面的多对多的定义大家更会清楚”多对多“与“一对多”的关系:其中我们注意<many-to-many/>标签的特点就知道,它是定义了一个多对多关系,而不是<one-to-many/>。
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC
"-//Hibernate/HibernateMappingDTD2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mappingpackage="org.hibernate.auction">
<classname="TestA"table="TestA"
dynamic-update="true"dynamic-insert="true">
<idname="id"column="id"type="int"unsaved-value="any">
<generatorclass="assigned">
</generator>
</id>
<propertyname="name"type="java.lang.String"
update="true"insert="true"column="name"/>
<setname="testBs"table="TestA_TestB"inverse="false"cascade="all">
<keycolumn="testA"/>
<many-to-manycolumn="testB"class="TestB"/>
</set>
</class>
<classname="TestB"table="TestB"
dynamic-update="true"dynamic-insert="true">
<idname="id"column="id"type="int"unsaved-value="any">
<generatorclass="assigned">
</generator>
</id>
<propertyname="name"type="java.lang.String"update="true"
insert="true"column="name"/>
<setname="testAs"table="TestA_TestB"inverse="true"cascade="all">
<keycolumn="testB"/>
<many-to-manycolumn="testA"class="TestA"/>
</set>
</class>
</hibernate-mapping>
在对多对中,因为一端维护关系另一端不维护关系的原因,我们必须注意避免在应用中用不维护关系的类建立关系,因为这样建立的关系是不会在数据库中存储的。基于上面的映射文件代码给出一个例子:
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();
}
测试后连接表的数据为:
testatestb
11
12
13
根据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){
}
}
那么测试执行后连接表的数据为:
testatestb
12
13
11
22
测试通过。