Inverse 是 hibernate 双向关系中的基本概念,当然对于多数实体,我们并不需要双向关联,更多的可能会选择单向关联,况且我们大多数人一般采用一对多关系, 而一对多双向关联的另一端:多对一的 inverse 属性是不存在,其实它默认就是 inverse=false. 从而防止了在一对多端胡乱设置 inverse 也不至于出错。但是 inverse 设置不当确实会带来很大的性能影响,这点是我们必须关注的。
这篇文章已经详细分析了 inverse 设置不当带来的影响:
http://www.hibernate.org/155.html (这个链接已经无效了,直接看下面的也可以使你能够较好的理解 hibernate 的 inverse )
看了这篇文章,还是很有必要再写下一些总结的:
1 ) inverse 中提及的 side 其实是指一个类或者表的概念,双向关联其实是指双方都可以取得对方的应用。
2 ) 维护关系这个名词还是稍显模糊或者晦涩。我们一般说 A 类或者 A 表(这里的表的是指多对多的连接表)有责任维护关系,其实这里的意思是说,我在应用在更新, 创建,删除(读就不用说了,双向引用正是为了方便读而出现) A 类或者 A 表时,此时创建的 SQL 语句必须有责任保证关系的正确修改。
3 ) inverse=false 的 side ( side 其实是指 inverse = false 所位于的 class 元素)端有责任维护关系,而 inverse = true 端无须维护这些关系。
4 ) 我们说 inverse 设立不当会导致性能低下,其实是说 inverse 设立不当,会产生多余重复的 SQL 语句甚至致使 JDBC exception 的 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></many-to- many> 标签的特点就知道,它是定义了一个多对多关系,而不是 <one-to-many></one-to- many> 。
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd ">
<hibernate-mapping></hibernate-mapping>
<class table="TestA" name="TestA"></class> dynamic-update="true" dynamic-insert="true" >
<id name="id" column="id" type="int" unsaved-value="any"></id>
<generator class="assigned"></generator>
<property name="name" type="java.lang.String"></property> update="true" insert="true" column="name" />
<key column="testA"></key>
<many-to-many class="TestB" column="testB"></many-to-many>
<class table="TestB" name="TestB"></class> dynamic-update="true" dynamic-insert="true" >
<id name="id" column="id" type="int" unsaved-value="any"></id>
<generator class="assigned"></generator>
<property update="true" name="name" type="java.lang.String"></property> insert="true" column="name" />
<key column="testB"></key>
<many-to-many class="TestA" column="testA"></many-to-many>
在对多对中,因为一端维护关系另一端不维护关系的原因,我们必须注意避免在应用中用不维护关系的类建立关系,因为这样建立的关系是不会在数据库中存储的。基于上面的映射文件代码给出一个例子:
package org.hibernate.auction;
import java.util.*;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class TestA {
int id;
String name;
Set testBs=new HashSet();
public TestA(){
}
public TestA(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestBs(){
return testBs;
}
public void setTestBs(Set s){
testBs=s;
}
public void addTestB(TestB tb){
testBs.add(tb);
}
public static void main(String[] args) {
}
}
public class TestB {
int id;
String name;
Set testAs=new HashSet();
public TestB(){
}
public TestB(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestAs(){
return testAs;
}
public void setTestAs(Set s){
testAs=s;
}
public void addTestA(TestA ta){
testAs.add(ta);
}
public static void main(String[] args) {
}
}
测试代码:
public void doTest() throws Exception{
TestA a1=new TestA(1);
TestA a2=new TestA(2);
TestA a3=new TestA(3);
TestB b1=new TestB(1);
TestB b2=new TestB(2);
TestB b3=new TestB(3);
a1.addTestB(b1);
a1.addTestB(b2);
a1.addTestB(b3);
b2.addTestA(a1);
b2.addTestA(a2);
Session s = factory.openSession();
s = factory.openSession();
Session session = 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 如 下:
/*
* Created on 2004-7-25
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package org.hibernate.auction;
import java.util.*;
/**
* @author Administrator
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class TestB {
int id;
String name;
Set testAs=new HashSet();
public TestB(){
}
public TestB(int id){
setId(id);
}
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public Set getTestAs(){
return testAs;
}
public void setTestAs(Set s){
testAs=s;
}
public void addTestA(TestA ta){
testAs.add(ta);
ta.addTestB(this);
}
public static void main(String[] args) {
}
}
那么测试执行后连接表的数据为:
testa testb
1 2
1 3
1 1
2 2
测试通过。