`
逆风的香1314
  • 浏览: 1390064 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

闲聊hibernate的inverse的引入

阅读更多
 

Inverse是hibernate双向关系中的基本概念,深刻理解inverse在hibernate中的含义对于掌握hibernate至关重要。
在讨论inverse之前,先说说一些闲话,侃侃hibernate怎样处理对象间的一对多或者对多对关系。这些闲话的前提假设是正确的设置了inverse。
看下面的代码:
//TestA.java
[/code]

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) {
 }
}
[/code]
//TestB.java
[code]
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;
 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;
 }
}


[/code]
TestA与TestB是一对多的关系,一是指TestA,多是指TestB,一个TestA可以联系着多个TestB。要将这个一对多的关联表述在数据库中,有数据库表关联的常识的人很容易想到在TestB表中用一个TestA_id列来表述TestA的主键。说得更直白一点:就是在TestB表增加一列TestA_id来表示他们所映射的对象之间的一对多关系。
上面的例子中,我们看到可以从TestA取得TestB对象集合,而TestB不提供获取TestA对象的方法,这说明一对多的关系由TestA来维护,TestB对这个关系什么都不管。可以通过下面的代码来附加描述维护关系的具体含义:
[code]
TestA a1=new TestA(1);
TestB b1=new TestB(1);
a1.addTestB(b1);
 session.save(b1);
System.out.println("save b");
[/code]
查看sql语句输出,只有一个insert语句,看数据库数据,仅仅testB表增加了一行数据,而Testa表和testB表中的testa_id为null。
我们需要注意的是,在save(b1)以前的代码中,我们已经明确的在内存对象中建立了a1与b1的关系,但是保存TestB对象时,这个内存对象中的关联并没有在数据库中得到反映,这就说明了TestB对象完全不关心对象的关系。这个其实是与我们代码中建立的单向一对多是完全吻合的。如果我们单单看TestB的代码,它只是个纯粹的没有与其他对象有任何关联,保存TestB对象数据自然只是纯粹的将它所拥有的数据插入数据库中。

接着看如下的测试代码:
[code]
TestA a1=new TestA(1);
TestB b1=new TestB(1);
a1.addTestB(b1);
 session.save(a1);
System.out.println("save a1");
[/code]
查看sql语句,发现有两个insert语句分别插入了testa 和testb表,查看数据库数据,我们发现testb中的testa_id列有了对testa的引用。说明内存的关系已经正确保存到了数据库中。
需要注意的是,代码中我们仅仅保存了a1对象,这说明testA对象确实在维护并在数据库中实现了对象间的关系。我们说,TestA有责任维护对象间的关系。
上面的例子虽然仅仅讨论了单向的一对多关联,但是它却给我们揭示了对象间的引用与对象具有维护关系的责任之间的对应,以及对象的维护关系的责任在保存数据时的具体意义。
我们修改TestB类代码,实现双向关联:
[code]
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;
                TestA  testA;

 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 TestA getTestA(){
                                return testA;
                }
                public void setTestA(TestA a}{
                                  testA=a;
               }
}

[/code]
根据上面的代码,我想问大家一个问题:
假设我们分别new了一个TestA:a1和一个TestB:b1,对于这两种操作:
1)a1.addTestBs(b1);
2)b1.setTestA(a1);
它们建立的关系你们认为是一样的吗?从内存数据来看,很显然,这两个操作的结果显然是不一样的。但是从面向对象的概念来说,他们都产生了同样的结果,那就是a1对象和b1对象建立了一对一的关联,注意:我这里讲的是两个对象的关系而不是两个表或者类的关系。也就是说,无论哪一种操作,反映到数据库中的关系都是同一个结果,在这点上,”两种操作导致的内存对象数据不一样“ 和“两种操作建立的对象间关系是一致的” 之间产生了一个非常明显的差异。很多人为了将这两个差距消除,他们往往这样修改TestA和TestB的代码:
[code]
//TestB.java
public void setTestA(TestA a}{
                                  testA=a;
                                   a.addTestB(this);
               }
//TestA.java
 public void addTestB(TestB tb){
  testBs.add(tb);
                                 tb.setTestA(this);
 }


[/code]
这样的修改虽然能够解决问题,但是现在讨论这些折中的修改并不适合我这里的讨论,把他们引入我的文章只会增加问题的规模以及复杂度。暂且放到我后面的文章再做进一步的讨论。
对于前面我所提出的两个操作,hibernate认为其中任何一种操作对于建立两个对象间的关联已经充分而完全,意思是说你执行任何一个操作都已经完成了对象的关联。
我们知道,在保存对象到数据库是,hibernate是根据对象内存数据来挖掘关系进而产生正确的sql语句执行,使得对象的数据和对象间的关系能正确保存到数据库中。
从上面的hibernate运行机理来看我们讨论的问题:任何一种操作所产生的内存数据,hibernate认为他们的关系是一样,即使两个内存数据不一样。这个时候问题来了,因为双向关联的需要,我们肯定需要这两个操作一起执行,我们在一般应用中会这样写代码:
[code]
TestA a1=new TestA(1);
TestB b1=new TestB(1);
a1.addTestB(b1);
b1.setTestA(a1);
[/code]
从hibernate运行机理来看,每保存一个对象,它就会检查该对象关联的任何一个对象,“如果有可能”,它就会继而处理被关联的对象,处理被关联的对象时,hibernate会重复前面的动作,说道这里,大家可能已经很清楚了,这就是级联操作。我说的“如果有可能”,就是指维护关系的怎认(inverse=false),其实在源代码中,它仅仅用一个if(invers标志)来执行这个判断。
先假设hibernate没有引入inverse的概念(很多人鼓掌相庆,那该死的inverse终于滚开了),那么hibernate运行机理就会变成:
从hibernate运行机理来看,每保存一个对象,它就会检查该对象关联的任何一个对象,它就会继而处理被关联的对象,处理被关联的对象时,hibernate会重复前面的动作。
大家注意到,少了“如果有可能”,呵呵。那么情况会变成什么样子呢?回过头来看看我们的上个操作,在hibernate应用中,我们把它补全:
[code]
[code]
TestA a1=new TestA(1);
TestB b1=new TestB(1);
a1.addTestB(b1);
b1.setTestA(a1);
session.save(a1);
[/code]
此时,hibernate会在我们假设的情况下这样运作:
它检查a1的内存数据,看到有一个集合,检查集合的数据,看到一个entry,检查该项,看到一个b对象,此时,hibernate认为这个关联已经成立,它会压入一个为建立关系而产生的语句:update testb set testa_id=? where id=?,在hibernate继而检查b对象时,它同样在b对象看到一个a对象的引用,这样的关联也要保存,于是它会在为建立这种关系产生相应的语句:Hibernate: insert into testb (testA_id, id) values (?, ?)。
从上面的分析我们知道,update testb set testa_id=? where id=?,在into testb (testA_id, id) values (?, ?)之后执行是多余的。
回到我们的问题分析中,归根结底,其实是双向关联的引入带来了这个重复建立关系的问题。为了解决双向关联中的这个特殊问题,hibernate引入了举世闻名的inverse属性,inverse属性正是为了避免重复产生关系建立语句以及提高对象关系的简洁,和SQL的优化。

好!到这里,我进入讨论 inverse正题前的闲话结束,接下来讲讲hibernate中invserse=false 和 invserse=true的含义。
待续》。。。。。。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics