论坛首页 Java企业应用论坛

沉重的负担-Hibernate的反模式

浏览 6261 次
精华帖 (0) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (7)
作者 正文
   发表时间:2008-11-02  

前言

虽然Hibernate作为最早流行的轻量级ORM,java社区的头号杀手应用,给数据库软件开发带来极大的便利和影响,但是在长期的实践中,仍觉得有些场景比较笨重,使用起来不甚顺手,遂发发牢骚。

 

本文意在抛砖引玉,如果你也有类似牢骚和解决方案,不如也发出来共享一下。

 

虽然本人有能力解决这些问题,也提到了以前开源的扩展模块,但并非刻意宣传,因为对第3个模式的不满致使我决定放缓对hibernate的修改,除非有外力支持,比如所在公司支持或者网友支持。

 

题外话,我正在抽空写以COM、XPCOM为基础的orm,语言是C++。COM可以实现反射功能,恰好用来写ORM,成品可以供支持COM的开发工具使用,也可以在js中使用,js中的orm不是梦想,当然各大论坛有些网友说用纯js写orm也是个可行的办法,确实值得一试,不过访问数据库的组件还是得用c/c++之类的来写,这个跑不掉。

反模式1:不支持模块化

像struts,可以将Action配置写到其他xml文件,然后再struts-config.xml里引用这些xml文件达到模块化的作用。

问题场景1:

当多个开发人员同时将几百个mapping标签写到hibernate.cfg.xml里面,而且还在不断增加的时候,文件冲突就是家常便饭。

建议解决方案:

直接修改hibernate,2年前俺在hbn-dyn-mod的第2个功能就这么做来着,但是经常加班加到熊猫样,还是业余时间搞的,又没有得到领导的支持,而且hbn-dyn-mod第1个功能开源后没什么人反响,所以连在sourceforge上更新的热情都没了,最后,连硬盘里的代码都丢失了...

 

问题场景2:

Hibernate.cfg.xml经常改动,在cvs,svn经常update,如果数据库名称和密码不同,可有得受了...

建议解决方案:

手工改改。

或者先读取hibernate.cfg.xml到内存中,从其他文件获取本机的数据库名和密码,并修改到内存中的hibernate.cfg.xml,然后再用修改后的内容启动Configuration,这样一劳永逸。

 

反模式2:不支持延迟加载映射文件

本来某段时间只专注一个模块的开发,但是做个单元测试也要等全部xxx个映射文件加载完毕,时间和内存消耗让本就不多的系统资源捉襟见肘,他就没有选项支持直到调用Session.get, load, save, createQuery.list等api时才读取对应的.hbm.xml吗?这也就算了,要是其他模块的开发者上传了一个错误的.hbm.xml或者持久化类,那就连运行都免了...

 

建议解决方案:

暂时屏蔽出错的模块对应的Mapping标签。

另外Hibernate2的话,可以试试hbn-dyn-mod。

反模式3:两套DOM模型

Org.hibernate.mapping包里的类,在Configuration里使用,可以完整的对应.hbm.xml里所有的信息,到了SessionFactory,就只能拿到org.hibernate.metadata.ClassMetaData了,其他几个模型类分散到其他包里,而且还不提供SessionFactory.getConfiguration方法,也太绝了点吧...

而且ClassMetaData的用法我实在搞不懂,也懒得研究。

建议解决方案:

懒得想,直接修改源码,给SessionFactory添加getConfiguration方法。

 

反模式4:不支持动态重载映射文件和持久化类

这个要求高了点,见仁见智了,由于jvm的局限性,估计有些人因此而转向了更灵活的python或ruby。当然,我可以解决这个问题,8过正如开头所说的原因,现在对修改hibernate没兴趣。

   发表时间:2008-11-02  
With Spring:

1.1 当多个开发人员同时将几百个mapping标签写到hibernate.cfg.xml里面,而且还在不断增加的时候,文件冲突就是家常便饭。
RE: mappingDirectoryLocations

1.2 Hibernate.cfg.xml经常改动,在cvs,svn经常update,如果数据库名称和密码不同,可有得受了...
RE: <context:property-placeholder location="classpath:jdbc.properties"/>使用${jdbc.driverClassName}取得值。



4....ruby在Product模式下似乎也还是要重启。
0 请登录后投票
   发表时间:2008-11-02  
LS说的对,用Spring可以解决部分问题。
第三点一般用不到;
第四点要求是高了。
0 请登录后投票
   发表时间:2008-11-03  
建议楼主认真学习hibernate。
第一点和第二点分明可以解决,何必抱怨hibernate不对,花点时间一找就搞定了。

另外个人认为:
“暂时屏蔽出错的模块对应的Mapping标签。”
是很扯淡的。

那请问需要在什么时候发现并解决错误?

对于第四点,在生产环境下,貌似这个完全用不上。
再说了新建了hbm.xml,还有相应的DAO、Service需要写,不重启服务就见鬼了。
每次都去搜寻一次更改效率更低吧。
0 请登录后投票
   发表时间:2008-11-24  
楼主说的问题,我在公司的项目中也深有体会。

3   不过第3点我比较赞同,因为时而发现hibernate的bug或项目中的特殊需要,需要修改源代码,去找相应的配置属性的调用方法要费九牛二虎之力,得分析sessionFactory,configuration等好几个类。

4  动态加载配置和映射,是一种热部署的思想,对于SOA架构,OSGi等也有这样的要求,我支持你;只是现在基于 xml 配置的方式,似乎厂商都不愿去动态化。

对其他问题,体会是一样的,特别是对于开发阶段,单元测试阶段,挺恼人的。

2  不过对于生产环境,第2点延迟加载配置可能有一定风险,还是要慎用,一次性加载便于发现错误的配置。


还有个问题,就是hibernate配置映射为的内存对象 Configuration, 极耗内存。可能一般的单数据库应用,table不太多的project,情况还不明显; 我们的移动项目,采用20多个库,每个库table很多有200多个,20个Configuration启动后占用了600M内存,1.5G的mx内存占了一大部分,很夸张!最后通过修改源码,采用共享机制得以化解。
结合 楼主的 第 2点,我觉得可以采用一次性全部验证, 而后采用Cache机制,内存中只维持最常用的一部分映射配置。


0 请登录后投票
   发表时间:2008-11-24   最后修改:2008-11-24
ClassMetaData 的作用:

基本用处看它的的名字,就是获取映射类的相关元信息,如果你要进行元编程,很有用。
 
另一个用处在于它的子实现类: AbstractEntityPersiter,通常用来获取tableName。这个在我修改hibernate 一个 query cache 的bug时有用到。

 AbstractEntityPersister persister =(AbstractEntityPersister) classMetaDatas.get(entityName);			 
String tableName = persister.getTableName();


参考:
解决Hibernate SQL Query Cache的一个可靠性问题(附源码)

0 请登录后投票
   发表时间:2008-11-25  
1. 关于模块化问师, 现在已不成为问题.
引入属性hibernate.archive.autodetection即可.

2. 关于延迟加载, 这的确是一个问题.
做JUnit test时, 很浪费时间. 我想应有相应的改进方法.

3. 关于两套DOM模型, 这是合理的
你要更详细的xml里的信息, 可以从Configuration中取得, 运行时期, configuration对象被丢弃其实不完全是hibernate本身的错, 只不过是spring或ejb之类的容器没有提供这个configuration对象的引用.
ClassMetaData提供运行时间的对象信息, 和xml中的不完全相同, 这里可以获得运行期的sql,对象约束,关系等.
所以, 这两套模型都是有必要的, 针对不同用途.

附:
关于configuration对象的获取, 在03年,纯hibernate的时代,我们会new Configuration(),所以很易保存这一对象用于数据分析. spring引入后, 我们不会涉及这一块的处理, 可以如下方式取得:
1) 普通的hibernate方式, 让spring自动注入, 可以你的类中加入:
@Resource
private LocalSessionFactoryBean sessionFactoryBean;
然后用:sessionFactoryBean.getConfiguration()
2) JPA方式(或者说按EJB规范创建), 这个麻烦点, 如果没有jdni支持, 无法获得这一configuration, 需重载spring的HibernateJpaVendorAdapter,或者, 用Ejb3Configuration重新构建一个.

4. 关于动态加载和持久化
这个是一个成为一个专题了, 网上有不少第三方解决方案, 但都不是很完美, 我也奇怪为什么gavin king不在hibernate本身加强这一方面的处理. 难道他反对(或者不看好)动态OO? 事实上, 不少应用有这方面的需求.
0 请登录后投票
   发表时间:2009-01-11  
引用

采用20多个库,每个库table很多有200多个,20个Configuration启动后占用了600M内存

20 个库 * 200 表 = 2000+表就算全部写在文件里,然后读到内存里,竟然占了600M的内存??,可以说说怎么算出来的吗
0 请登录后投票
   发表时间:2009-01-12  
送你四个字:

单元测试.
0 请登录后投票
   发表时间:2009-07-10   最后修改:2009-07-10
EXvision 写道
建议楼主认真学习hibernate。

对于第四点,在生产环境下,貌似这个完全用不上。
再说了新建了hbm.xml,还有相应的DAO、Service需要写,不重启服务就见鬼了。
每次都去搜寻一次更改效率更低吧。


我倒是期望能有一个按需加载的特性,金蝶自己搞的ORM里就有这个一个东西,比如说这次我load一个东西的时候和hbm里面定义的延迟加载有所不同,在加载A的时候需要同时将A.b也加载出来。不过初步意向这个东西可以通过封装一个HQL查询来实现。


另外,HQL不支持union等常见集合操作很郁闷,牛人总是抛出一句话,可以用sql来解决。。。

有人也提了好久了,到现在都没个说法,JIRA地址:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1050
0 请登录后投票
论坛首页 Java企业应用版

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