技术开发 频道

浅谈Hibernate的学习经验



三、       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将打印出它的堆栈踪迹。通过检查堆栈踪迹可以发现第一次请求此连接的代码,然后可以通过检查代码而找出没有释放连接的原因。
0
相关文章