java项目开发实践经验之一:使用hibernate的几点经验总结(转)
转自:http://www.iteye.com/topic/724586
?
使用java进行项目开发也有不少年头了,应用从网站到金融产品到自动页面检索,使用的开发框架从最初的jsp+javabean到ejb再到ssh,数据库从mysql、sql server到oracle、informix、db2,查询语言从sql到xquery...经历的风风雨雨,其间无数次的问题,无数次的功课,现在看来也颇令人感慨,想想觉得应该还是留下些什么,和大家分享一下自己的心得,也欢迎多交流,多指教。
还是以hibernate开篇吧,毕竟它很热,而且自己对它也还是比较有感情的。说实话heibernate这个东西,在编码的时候确实让人很贴心,尤其在ssh框架中,它的出现使得面向对象的技术和数据库结合的是那么的自然,而且节省了大量的重复编码,但是且慢,如果你不能避开它给我们留下的那些陷阱,则后果也是很严重的。
经验一:合理使用lazy load
延迟加载这个东西确实很方便,尤其配合opensessioninview的技术,往往一个对象即表数据以及所有关联表的数据都直接在页面上通过pojo关联
的形式带出来。不过随之带来的是数据库查询压力增大的风险。设想以下场景:主表a,子表b,在hibernate中A(a表)对象与B(b表)对象一对多关联
并且设置了延迟加载,即A对象包含了一个B对象的列表(List),假设在一个方法中需要针对A对象中的所有B对象进行业务条件判断,如下列代码
示例:
?????? function void someFunc(A a){
?????? Iterator it = a.getBList().iterator();
?????? while(it.hasNext()){
?????? B b = (B)it.next();
?????? if(b.getType()==1){
?????? //some code
?????? }
?????? }
?????? }
当循环判断b的type属性时,每个循环都会产生一个新的sql查询,如果有1万个子表记录则需要查1万次,表面上看只是一个属性判断,其实里边的
陷阱还挺深
解决方案:
首先如果你确定延迟加载的表对象不会产生类似上述的风险,比如在任何业务场景中都不会进行循环判断或对应的子表关联记录数只有很少,那么并
不需要做改变。
其次如果确实有可能由以上的场景风险存在,那么就可以对那些场景使用的对象使用非延迟加载,比如上边的示例中的参数A对象我们就不是直接使用
load而是使用find方法,并在方法中使用hql的时候进行left join fetch关联,如:getHibernateTemplate().find("from A a left join fetch a.bList b");
取得了这样的A对象再进行循环判断的时候当然就不用再到数据库查询了
经验二:合理使用opensessioninview
opensessioninview当然并不是hibernate的而是spring的一个技术,之所以列在这里因为基本上他就是为了hibernate服务的。
opensessioninview使得web应用中hibernate的session可以一直保持到页面显示,从而在jsp中可以方便的使用pojo的模式显示对象属性信息。
如在servlet或action中把a对象放入request,而在jsp中可以通过:<c:out value="${a.b.type}"/>就可以显示与a对象关联的b对象的type属性。
但正是由于session一直保留,因此在一些并发量比较大并且数据量又很多的情况下,很容易造成数据库链接无法释放的问题。并且在需要长时间
运算的web页面,这个问题会更加突出,试想以下场景:你有一个根据数据库相关记录信息生成数据包(多个xml文件组成)的业务,你点击页面上的
一个按钮、然后点一根烟,当烟抽完的时候数据包就会生成了,如果这个页面你使用了opensessioninview,你会发现一包烟都抽完了数据包仍然
不见踪影,然后发现数据库当前链接有n多都没有释放,原因?opensessioninview使得页面在运行过程中一直保持数据库链接不释放造成的。
解决方案:
对那些数据压力大的或需要长时间加载的页面不使用opensessioninview,可以通过扩展名进行区分筛选,如:特殊页面的链接url使用xxx.do这个
形式,而*.do的扩展名在web.xml不使用opensessioninview进行过滤
经验三:自增主键生成模式
hibernate默认的自增主键生成方式是increment,即由hibernate来管理进行自增,不过一定不要在项目中使用默认方式,除非你确认不会使用
其他如jdbc方式进行数据相关库表的数据操作,以及确认不会有集群或多个应用添加同一个数据库表。。。显然在一个正式的项目中increment
模式不可行,推荐使用assigned即自定义模式,对主键的生成进行统一调配,这样才可以在不同数据库间兼容,当然项目对应的数据库类型
已经确定了,使用identity模式(DB2、SQL Server、MySQL)或sequence模式(oracle)也是很方便的
经验四:反转控制
有一对多关联,最好忘记设置反转控制,否则如果子记录成千上万,主对象一旦被load又没有设置延迟加载,数据库的压力可就。。。
经验五:hibernate实体对象传递
很多情况下需要把hibernate实体对象拷贝成另一个继承实体对象的子对象,传递对象数据很麻烦,一个方式是用n个set方法干体力活,当然
还有BeanUtils这样贴心的工具类,但别忘了加上忽略的属性才能拷贝成功哦:
String[] IGNORE_HIBERNATE_PROPERTIES = new String[]{"callback","callbacks", "hibernateLazyInitializer"};
BeanUtils.copyProperties(hbo,target,IGNORE_HIBERNATE_PROPERTIES);
先写这么多吧,打个小广告^-^:轻松阅读、快乐享受,尽在“阅读地带” www.article-reading.com