论坛首页 Java企业应用论坛

JDO和Hibernate的比较 – Persistence By Reachablilty

浏览 16250 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2004-12-11  
Hiberante目前不支持默认的Persistence By Reachability,但是支持通过在xml配置文件中的显示定义来支持Persistence By Reachbility。而JDO则默认支持Persistence By Reachbiity。


Hibernate在文档中这样解释: Hibernate does not fully implement "persistence by reachability", which would imply (inefficient) persistent garbage collection. However, due to popular demand, Hibernate does support the notion of entities becoming persistent when referenced by another persistent object.

Hiberanate不全面支持persistence by reachability是因为可能到导致内存中的垃圾回收问题,意思就是可能导致内存溢出问题。persistence by reachability既然是一个很受欢迎的要求,为什么不干脆实现默认的Persistence By Reachability呢?这是由于目前的Hibernate架构不能很容易的实现,需要对现有的架构做很大的变动才能象JDO那样默认支持persistence by reachability。

我们一起看看Hiberante得架构为什么不容易实现persistence by reachability。

在net.sf.hibernate.impl. SessionImpl中,所有由当前session保存/更新的Persistence Object都需要临时保存在一个entityEntries的Map中,而在Map中的PO都是以strong reference的形式保存。如果Map的对象数目不断增长,就有内存溢出的可能。而entityEntries在session.clear()和session.close()中才会被清除。但如果保存得时对象图而不仅仅是对象树,session.clear()是不建议在每次flush后使用的。因此在处理复杂一些的关系是通常只有依赖session.close()来释放entityEntries。


如果在打开persistence-by-reachability的情况下不每次获得一个新的session和关闭session,执行这样的pseudo code:
School school = new School(..);;
for( int index = 0; index < Integer.MAX_VALUE; index++ );{
    Department dept = new Department( index, school );;
    Profile studentProfiel1 = new Profile(…);;
    dept.addStudent( new Student(studentProfiel1,…); );;
	
    Profile studentProfiel2 = new Profile(…);;
    dept.addStudent( new Student(studentProfiel2,…); );;
	
    Profile studentProfiel = new Profile(…);;
    dept.addStudent( new Student(studentProfiel3,…); );;
     ……
   dept.addStudent( new Student(studentProfiel200,…); );;
     session.save( dept );;
     session.flush();;
}


即使在上面的代码中的reference已经可以被回收,但由于这些PO instances仍然在entityEntries被strong reference,所以无法被jvm做garbage collection。如果Department ,Student, Profile, School内容多,就可能出现内存溢出。

而在web的应用中,在一个用户的http session中一直保持hibernate session打开,执行各种插入更新操作,如果用户多的情况,也会导致entityEntries中的对象逐渐增加而内存溢出。

由于没有执行session.clear()的机会,就有可能导内存溢出的情况。因此在复杂的对象图(object-graph)的处理中,打开Persistence By Reachability是有潜在的内存溢出的危险的。当然,通过在应用层的小心的架构(比如把session开始/关闭封装在每个http请求中),这些内存溢出的情况是避免的。

而在JDO中,其本身的架构就考虑了对Persistence By Reachability的支持。PO在PersistenceManager中在完成了操作后是用weak reference来指向的。一旦在应用层reference可以被回收,PO instance就可以被jvm回收,而无需显式地关闭Persistence Manager,因此如果执行上面代码的JDO版本不会有内存溢出的危险。

而在web的应用中,JDO对于在每个request后关闭当前的Persistence Manager也没有限制,用户可以在整个http session中保持PM打开。Persistence Manager是否关闭取决于用户自己应用架构的需要。

如果要在Hibernate中实现Persistence By Reachability,就需要在session的实现中引入weak refernece。但这样的改动对Hibernate来说实在太大了,和获得好处(支持Persistence By Reachability)有点得不偿失。所以Hibernate也不着急实现这个feature,而让用户在xml中显式的定义Persistence By Reachability,这也是对用户的一个提醒,让用户在应用中合适的架构避免潜在的内存溢出问题。
   发表时间:2004-12-11  
这个weak reference/strong reference的问题可以引伸到另外一个问题,就是hibernate为什么不实现property的lazy load。不是不想,同样是代价太大。另谈。
0 请登录后投票
   发表时间:2004-12-11  
1、为什么Hibernate从一开始就不使用weak reference呢? 而采用strong reference的呢?这恐怕不能单单用实现方便来解释吧?

2、把Hibernate Session在Web层HTTP Session中传递是一个很坏的实践,把Hibernate代码偶合到Web,业务各层代码中去了,并且HTTP Session的失效就会导致Hibernate Session无法取得,也无法被关闭。无论如何这都不能成为一个理由。

即使是JDO的PM,也适用同样的道理,抛开节省了那一点点持久对象引用之外,假如你在HTTP Session中一直不关,很快你的数据库就爆掉了,即使你的JVM不会泄漏内存又有何用?

当代的业务层框架的良好支持,例如SpringFramework已经可以让你非常简单的进行Hibernate/JDO的资源管理,使得你不必再操心Session/PersistentManager的打开和关闭问题。

3、你前面举的Persistence by Reachbility的例子缺乏足够的说服力。对于使用Hibernate的程序员来说,写HBM配置的时候,cascade不会不写update-delete/cascade all的,这就好像Hibernate的lazy loading,默认是不打开的,但是不会有什么人不打开。反正HBM配置文件我总是要写的,那个小小的配置项并不会增加我什么工作量,我想不出来为什么多写这个一个配置项会成为Hibernate不够好的理由之一。再说比较一下代码:

Hibernate: 

secodeSession.update(cat);  // cascade update mate


JDO: 

firstPM.detach(cat);
...
secodePM.attach(cat);


首先我不觉得session.update有什么不如PM.attach更不爽的,其次,对于JDO来说,我要先detach,然后传给Web层,最后回来的时候我再attach,而Hibernate完全不需要手工进行这种PO的attach/detach。想像一下对整个PO的object graph进行attach/detach,My God! Rod John在《withou EJB》这本书里面也提到了JDO的这个问题,实际上手工进行对象的attach/detach是非常痛苦的事情,有时候我可能未必知道哪些对象都关联起来,可能我仅仅为了需要某个对象,我就需要对n个对象进行attach/detach操作。
0 请登录后投票
   发表时间:2004-12-12  
引用

1、为什么Hibernate从一开始就不使用weak reference呢? 而采用strong reference的呢?这恐怕不能单单用实现方便来解释吧?

文章中没有对采用weak/strong reference是否实现更简单做结论。你可以自己作结论。如果用weak reference,架构会完全不一样。你可以自己尝试架构一下。但Hiberante自己说明采用了persistence-by-reachability会导致insufficent garbage collection,为什么呢?为什么JDO中没有这个问题?这是文章想要对比双方的架构给出答案。


引用

2、把Hibernate Session在Web层HTTP Session中传递是一个很坏的实践,把Hibernate代码偶合到Web,业务各层代码中去了,并且HTTP Session的失效就会导致Hibernate Session无法取得,也无法被关闭。无论如何这都不能成为一个理由。

给出的例子是说明Hiberante Session在何种情况下会出现问题,文章中也说明了在应用层合理架构可以避免。 take it easy, bro

引用

即使是JDO的PM,也适用同样的道理,抛开节省了那一点点持久对象引用之外,假如你在HTTP Session中一直不关,很快你的数据库就爆掉了,即使你的JVM不会泄漏内存又有何用?

实事上PM并不象hibernate session一样和 jdbc connection紧密耦合的,保持PM打开不会爆掉数据库。



引用

对于使用Hibernate的程序员来说,写HBM配置的时候,cascade不会不写update-delete/cascade all的

既然如此,为什么不干脆默认支持。文章要说明的是这个问题。而不是争论好坏。好不要好用,是很主观的东西。这里存粹是技术比较。

引用

有时候我可能未必知道哪些对象都关联起来,可能我仅仅为了需要某个对象,我就需要对n个对象进行attach/detach操作。

我想你被误导了吧。attach/detach是同样遵行persistence-by-reachablity的原则,你只需要detach/attach 一个root class就可以了。

这个是技术论坛,我尽量不做商业式的评价,如果你认为我对hibernate架构的解读是不正确,请纠正。
0 请登录后投票
   发表时间:2004-12-12  
我不了解JDO的运行机制,但是Rod Johnson在《Without EJB》是这样评价JDO的。你提到JDO使用弱引用,得以避免很多问题,例如内存泄漏,不必关闭PM等等,特别是无须关闭PM,我还是觉得有点不可思议。

你是站在框架的编写者的角度来看问题的,而我是站在框架软件的使用者角度的,我并不关心框架的运行机制和原理,我也不关心什么persistence-by-reachability理论,我只关心框架使用简单和易理解,比较一下实际的代码:

Hibernate: 

secodeSession.update(cat);;  // cascade update mate 


JDO: 

firstPM.detach(cat);; 
... 
secodePM.attach(cat);;   // cascade update mate 


Hibernate不需要对PO显式的attach/detach,而JDO必须对PO显式的attach/detach,我不知道这是否和Hibernate是强引用,而JDO是弱引用有关系。想对应的是Hibernate必须对detach过的PO显式的update,而JDO不需要,如果这种差异是由于强引用/弱引用的差别带来的话,那么Hibernate不使用弱引用的原因就不单单是因为上面提到的那些原因,更可能是因为GK觉得PO的attach/detach比较烦琐和丑陋,而代价就是显式的update。

在这两种方式中,不知道大部分程序员更倾向于那种方式,是显式的update一下cat对象;还是显式的attach/detach一下cat对象,而我,是比较反感显式的attach/detach这种方式的。

说到property的lazy loading问题,我原先是倾向于增加这种功能的,毕竟可以带来很多便利,然而最近我却有不同的看法。实际上lazy loading是一把双刃剑。lazy loading固然可以避免一些必要的性能问题,然而在分层应用程序中,特别是包含了分布式调用的程序中,PO往往要在session关闭以后被传递给业务层,业务层拿到PO以后,session已经关闭,lazy的属性和关系已经无法取得。所以一个在性能和架构的权衡考虑之后的方案就是仅对必要的场合采用lazy loading,我认为大部分情况下也就是仅对one-to-many关系lazy loading,而把parent.getChildren()这种PO的lazy loading的方法在DAO接口层再封装一次,把它单独放在一个Session中初始化,则可以解决上述的种种问题。而many-to-one, one-to-one只会产生单条join的sql,则不必为了增加程序复杂度而使用lazy loading。

对于property则同理,试想一个对property lazy loading的PO被传递给业务层,甚至Web层/Desktop 层,特别是Desktop层,你无论如何都无法再取得被lazy loading的property的,势必向数据库发起另一次查询请求。对于对象关系的lazy loading,客户端程序员通常可以很容易遵循这样的约定:即直接取得PO属性,而关系则通过DAO取得,对于对象属性的lazy loading,客户端程序员很难区分哪些属性被lazy loading,哪些属性没有被lazy loading,从而进行不同的操作。

因此,虽然Hibenrate3支持了property的lazy loading,我仍然不认为使用property的lazy loading是一种好的架构策略,而我则认为碰到这种情况,Hibernate Lightweight class pattern是最好的选择。
0 请登录后投票
   发表时间:2004-12-12  
引用
我不了解JDO的运行机制,但是Rod Johnson在《Without EJB》是这样评价JDO的。你提到JDO使用弱引用,得以避免很多问题,例如内存泄漏,不必关闭PM等等,特别是无须关闭PM,我还是觉得有点不可思议。

Rod Johnson评价的是JDO 1.0的makeTransient方法。也提到JDO2会提供新的方法来detach对象图,这就是JDO2的detach/attach。不关闭PM没什么不可思议的,不同的架构自然有不同的使用方法。在hibernate session中,jdbc connection是作为contructor的参数传入,每个session都会引用一个jdbc  connection直到session.close(),自然需要关闭。JDO PM只在用户执行操作的时候才会占用jdbc资源。所以不需要。

引用
更可能是因为GK觉得PO的attach/detach比较烦琐和丑陋,而代价就是显式的update。

我不猜测GK如何想。JDO2的detach/attach是学习Hibernate的思路,GK架构Hibernate的时候JDO2还没出世呢。我觉得讨论不应该带有太多的个人情绪,就事论事。如果robbin你批评JDO,而且给出具体JDO不适用的例子,我会很认真的听,并尝试在我们的产品中去解决他们。


引用
还是显式的attach/detach一下cat对象,象,而我,是比较反感显式的attach/detach这种方式的。

个人喜好。无法评论。attach/detach如果关系复杂还是很好用,比如一个对象有好几个references,还有collection。可以同过persistence-by-reachablity来自动的分离和attach object graph.好不好用,是个人喜好。但JDO2这个的重要功能使JDO能适合web多层开发的需要。如果真要比较,那就是JDO的PM在Open Session in View pattern中没有hibernate session的那些限制。


引用

说到property的lazy loading问题,我原先是倾向于增加这种功能的,毕竟可以带来很多便利,然而最近我却有不同的看法。实际上lazy loading是一把双刃剑

lazy load是一个很常用的需求。由于JDO PM架构不同,没有Hibernate session的某些限制(关闭session etc)。即使是需要关闭PM,也可以通过runtime fetch plan很好的管理lazy load。比如在目前我们移植的一个项目中,一个表居然有多达40多个字段,还有字段事clob。在列表页面,我只需要显示几个字段,在详细页面需要显示大部分字段。如果有property lazy load就很方便,如果能有动态的fetch plan,在列表页面使用一个简单的fetch plan,在详细页面使用另一个详细的fetch plan,那就更方便了(在即将发布的liberator 1.2正式版可以见到这个功能)。这些不同的plan可以在detach和load的时候根据需要动态指定。

引用


因此,虽然Hibenrate3支持了property的lazy loading,我仍然不认为使用property的lazy loading是一种好的架构策略

为了支持property lazy load而多写代码和配置文件,对我来说工作里还是多了。作为开发人员我是很懒得,如果persistence-by-reachailtiy是一个很常用的功能,我就希望事默认的。配置文件也一样,我们的原则是能省则省,常用的能默认就默认。配置文件越简单越好,如果一个对象的field能默认映射到表的column,就不需要写&lt;field.../&gt;之类的。

Hiberante3为了支持property lazy load付出了代价,就是必须采用build-time enhancement。Hibernate2不支持就是当时的代价大。

另外就是Hibernate 3,3.1中相当一部分功能都在JDO2中有雷同的定义,你会发现未来的JDO2产品会和Hibernate3功能上越来越象。最后自然都统一到ejb3去了。

Hibernate是一个很优秀的产品,但也不是没有任何缺点,太过神化没有太多的意义,也不利于技术的进步。JDO2我认为是一个相对宽容和勇于学习的EG。
0 请登录后投票
   发表时间:2004-12-12  
原来如此,那么偶是不是可以这样看:
hibernate如果把cascade的默认值从现在的none改到all,不就和JDO一样了么?只是因为这样有潜在的memory问题,所以hibernate才使用none这个默认值?
0 请登录后投票
   发表时间:2004-12-12  
我对JDO可没有什么排斥情绪,我只是表达我做为一个框架使用人员的感受。由于数据库的操作通常就是CRUD,对应持久对象的save, delete, update, load的操作,因此要求程序员写一个 session.update(PO)对于大多数人来说是很自然而然的事情,事实上我知道绝大多数使用Hibernate程序员,即使在同一个Session内部往往也习惯的写一个session.update(PO),虽然这并不是必要的。而如果要求大家在每次PO在脱离PM之前都显式的detach,当PO进入PM的时候都attach,恐怕很多人都会感觉不舒服。

从你上面对于JDO的介绍,我是否可以认为PM只管打开,而不用关闭。PM每次在实际操作的时候才从连接池获取Connection实例,至于什么时候把Connection归还连接池则是JDO自动管理,所以基于JDO的应用程序,我们只管打开PM,而永远也不需要close呢?
0 请登录后投票
   发表时间:2004-12-13  
引用

而如果要求大家在每次PO在脱离PM之前都显式的detach,当PO进入PM的时候都attach,恐怕很多人都会感觉不舒服。

正如你指出,weak referenc/soft reference的运用导致Hibernate/JDO在使用上有些不大差异。对于detach/attach的运用,attach等同于update/updateOrSave, 但比hibernate确实要多一步detach. 在JDO中是这样理解,detach前object graph里都是PO,detach后object graph是VO, attach则相反。多一个步骤虽然可能不舒服,但如果detach配合runtime fetch plan,可以根据不同的UI层需要,detach不同的sub-object graph,还是有它的价值。

引用
至于什么时候把Connection归还连接池则是JDO自动管理,所以基于JDO的应用程序,我们只管打开PM,而永远也不需要close呢?

是,PM中connection等资源都是用weak reference的方式管理。一旦用户应用中不在strong reference Query/Query的结果,就会被自动garbage collect。PM主要占用内存资源。但在应用中,我们建议用户显式关闭query的结果集( 一个Collection ),而不是依赖JVM的gargbage collection来回收Query的结果Collection,因为Collection的implementation strong reference a jdbc  connection。
0 请登录后投票
   发表时间:2004-12-13  
谢谢Charles。那么是否因为JDO采用WeakReference,所以:

1、必须显式的attach/detach PO,而无法像Hibernate那样自动的attach/detach呢?

2、一个未被持久化过的PO,我只要PM.attach(PO),他就自动被insert到数据库呢?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics