技术开发 频道

浅谈Hibernate的学习经验



    【IT168 专稿】本文并没有讲述如何配置及使用Hibernate,因为已经有太多网站提供此类的资源(Hibernate的官方网站是最好的学习网站)。通过本文,希望跟读者分享笔者在学习及使用Hibernate过程中的经验、心得及一些思考,希望读者在学习与使用Hibernate时尽量少走弯路。是最好的学习网站)。通过本文,希望跟读者分享笔者在学习及使用Hibernate过程中的经验、心得及一些思考,希望读者在学习与使用Hibernate时尽量少走弯路。
 
一、    喜忧参半
 
       当项目使用Hibernate时,就笔者的经验而言,存在可喜的方面,同时也有不尽如人意的地方。不如人意的地方是Hibernate是一个很庞大的软件,而且设计难度可能远远超出初学者的想象范围。可喜的是它又可以仅仅像你所期待的难易程度一样,简单的配置就可以使用。

       总的来说,使用Hibernate越频繁,则它的优势越明显,Hibernate的设计思想也会越遍及应用程序的架构。当然,如果读者不能充分的理解Hibernate将带来什么的话,由于使用Hibernate而存在的隐患风险无疑也会增大。

       关于Hibernate,初学者须知,它并非唯一的对象持久层框架。也就是说,有很多其它的框架也可以实现如同Hibernate一样的功能,但其它的框架有可能需要在某些因素如正确性、程序性能及简洁性等方面付出较大的代价。
 
二、       理论VS实践
       众所周知,Hibernate能很高效的运行,最主要依赖于一个能将它所使用的对象全都加载进内存的运行环境,于是,使用者可以随时地提取所需的对象,这就使得使用者需要维持内存中的对象树。但事实上,这样的运行环境常常显得太过于理想化。
       这是因为,如今的许多操作系统大多是无状态和(或)分布式的操作系统。例如,绝大部分的界面用户都使用HTTP协议,而它是一种无状态的协议。当然我们可以获得HTTP的Sessions,但只有小部分且受限的信息可以安全的保存在sessions中(也许这正是Gavin King建立Seam的原因所在)。
       在无状态的操作系统中,使用Hibernate需要付出额外的努力。这是因为HTTP与面向对象之间存在错配问题,而不是Hibernate。Hibernate可以提供非常好的对象/关于映射,但这不意味着它也可以提供很好的HTTP/面向对象的映射。


三、       Hibernate性能
众所周知,n+1次select查询问题(select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题,这种检索策略没有利用SQL的连接查询功能)对EJB而言也是普遍存在的,Hibernate提供了避免此问题的机制。但如果使用者不够仔细,很有可能在此问题上犯错。
(1)      请记住:它是基于JDBC的框架
我们知道,Hibernate最终是需要生成JDBC statements。大量的工作都是围绕自动的生成statements而展开的,当然也可以手动的生成。其关键点往往不在于statements的内容,而在于statements的数量以及被执行的序列数(sequence)。
请考虑如下例子:一个多记录插入的批处理程序。理论上Hibernate可以完成批插入。但是,每条插入的记录都需要使用序列生成一个唯一的ID。由于ID序列生成器被占用,Hibernate将选择性执行插入的statements和序列选择的statements。这导致Hibernate无法完成在单一调用(single calls)中执行批插入statements的任务。
为了避免此种情况的发生,Hibernate使用seqhili ID生成器来生成ID。它实现了在单一调用中产生一系列的序列ID值。生成的系列ID值将分配给不同的插入记录。如此则可实现Hibernate批插入处理功能。来生成ID。它实现了在单一调用中产生一系列的序列ID值。生成的系列ID值将分配给不同的插入记录。如此则可实现Hibernate批插入处理功能。
(2)      请注意:它是基于数据库的框架
请考虑如下例子:一个批处理过程程序,从某一文件读取大量记录,并将记录插入到数据库中。但是,在插入记录之前,需要先检查数据库中是否已经存在此记录。如果存在,则需先删除存在的记录后再插入新记录。
首先想到的实现方式是采用HQL语句来删除记录。但这将导致Hibernate执行对数据库的交互性statements,从而没有可能批量插入,并使程序性能下降。所以,唯一且高效的方式应该是:在数据插入之前,在数据库中编写一个触发器来删除旧记录,用它来代替上面的HQL删除语句。
(3)      连接池
连接池是一种实现程序高性能,特别是在大型交互式程序中,且最简单的方式。默认的情况下,Hibernate采用一个很小的内部连接池(详情请见此),但实际应用中并不常用。因为它有可能导致程序性能的退化。它最主要的问题是如何选择连接池的大小。所以,Hibernate往往采用第三方的连接池,如c3p0
 
四、 调试Hibernate
(1)      日志
由于Hibernate进行了封装,因此调试Hibernate应用程序显得很困难。而通过Hibernate的日志则是了解Hibernate运行状况的非常好的方式。但是,日志也只能选择性的使用,不然,可能陷入信息的沼泽地。
例如,大家普遍通过设置Hibernate的属性.show_sql=true来查看SQL语句(详情请见此)。但是,不能查看SQL语句中绑定到参数的具体值。如果使用log4j的话,则可以通过在log4j.properties文件中加入log4j.logger.org.hibernate.type=debug来实现以上目的。关于Hibernate日志信息的更多信息,可以访问此处
(2)      连接漏洞
一个最常见的问题就是数据库的连接漏洞。而在使用Hibernate时,往往是由于前边打开了连接数据库的session,但却忘记关闭它而造成的。
笔者偶然发现查找此漏洞根源的方法:DBCP连接池。Tomcat采用DBCP来构建。DBCP通过若干参数(removeAbandoned and logAbandoned parameters)来动态回收废弃的数据库连接。这可以避免连接漏洞产生新的问题,但却不能解决更深层次的问题。
读者可以通过激活DBCP的 logAbandoned 参数来查找导致连接漏洞的代码。这样可以使DBCP记录它处理连接时的堆栈踪迹(stack trace)。如果数据库连接到后来作废并且销毁了,则DBCP将打印出它的堆栈踪迹。通过检查堆栈踪迹可以发现第一次请求此连接的代码,然后可以通过检查代码而找出没有释放连接的原因。


五、    Hibernate工具
关于生成Hibernate代码的工具种类繁多,例如Hibernate Tools、Hibernate Synchronizer、MyEclipse的Hibernate功能等等。而且,许多工具宣称支持双向工程代码(round-trip Engineering)自动生成,也即允许使用者在更新数据库模式之后,可以重新生成域对象代码,而不用覆盖任何已经添加的业务相关的代码。
但请不要过于信任这些工具。在处理较大的数据库模式或数据表时,用于一次性的代码生成,它们也许可以胜任。然而,这些工具往往不能生成我们真正需要代码,因此需要大量的修改。例如,双向生成器在修改代码时有很严格的要求,常常造成代码不必要的重大变动。
而且,当数据库模式不一样时,许多Hibernate工具表现得很奇怪。例如,使用Hibernate Tools为一张没有主键的数据表生成域对象时,它会错误的认为表中的所有字段均为主键,从而自然地生成一个复合的主键。
 
六、  Hibernate Annonations
在最近的Hibernate版本里,一个新的建立在Java 5 注解之上更为优雅的方法出现了。利用新的Hibernate Annonations ,读者可以发布一次如以前的映射文件所定义的信息,注解直接嵌入Java类文件里。注解带来了一种强大灵活地声明持久化映射的办法。最新版的几个Java集成开发环境里都很好的支持,并带有代码自动完成和语法高亮功能。,读者可以发布一次如以前的映射文件所定义的信息,注解直接嵌入Java类文件里。注解带来了一种强大灵活地声明持久化映射的办法。最新版的几个Java集成开发环境里都很好的支持,并带有代码自动完成和语法高亮功能。
Hibernate annotations 也支持最新的EJB 3持久化规范,这些规范目的是提供一个标准的Java持久化机制。当然Hibernate 3也提供了更多的解决方案,能非常容易的靠近并且利用EJB 3编程模型编写Hibernate持久化层。
尽管如此,在使用Annotations库之前,若干重要的因素需要给予足够的重视:
1.         Annotations库需要Java5的环境支持;
2.      在笔者写此文时,Annotations库只是预发版本(pre-release version)。也就是说,随着EJB3标准的最终发布,Annotations的最终版本也将发行。同时,从笔者的使用经验情况来看,此预发版本也还算稳定。
3.        学习Annotations库的过程曲折。这主要是因为关于它的文档尚不成熟。如果读者是初学Hibernate,则使用Annotations库将显得更加困惑。
4.       工具对它的支持稍显滞后(尽管Hibernate Tools可以成功的用于生成对域对象的注释)。
 
七、    Hibernate的部署
对于部署Hibernate时,从安装的Hibernate里选取所需的JAR文件是难以确定的。于是很多人在部署Hibernate时,将安装Hibernate里的JAR文件全部选取。这样做会引发如下的一些问题:
1.         延长从CVS上获取整个工程的时间。
2.        毫无必要的增加了部署文件的大小,例如EAR、WAR文件的大小。
3.        增加了操作系统设置classpath的复杂度。
4.        在开发环境下增加了不必要的文件,使开发人员眼花缭乱。
可行的方法之一是只添加所需的文件,在运行的时候查看程序是否中断。那么哪些文件是部署Hibernate所必需的文件呢?在Hibernate的安装文件包下有一个名为lib/_README.txt的文件,这里列出了Hibernate所有的JARS文件,同时说明了它们在什么情况将会被使用。笔者发现,此文件对于保持部署文件的大小是非常有帮助的。
0
相关文章