- 浏览: 499005 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (258)
- 0-中医 (83)
- 1-工作 (4)
- 2-生活 (17)
- 3-其他 (3)
- oracle_dev (5)
- oracle_dba (35)
- ebs_gl (1)
- ebs_ap (0)
- ebs_po (0)
- ebs_hr_people (0)
- ebs_hr_payroll (0)
- java (12)
- javaScript (7)
- JSP2.0 (4)
- springMVC (3)
- spring (4)
- iBatis (5)
- Hibernate (3)
- tomcat (2)
- linux (13)
- 网络 (3)
- python (25)
- Django (11)
- z-技术 (13)
- PHPCMS (0)
最新评论
-
bo521dai:
Bravo. contains everything.
Oracle调优总结 -
yangxiutian:
固态硬盘是什么东东,既然对硬件有约束,我想推广难啊,除非若干年 ...
未来操作系统(组图) -
showzh:
...
listener.ora 、sqlnet.ora 、tnsnames.ora的关系以及手工配置举例 -
489687009:
我特别想问一下楼主,现在有了框架后,jsp2.0还有用武之地吗 ...
JSP2.0入门 -
liuzl121:
你好 我刚学java,我想请教下这个SignonControl ...
log4j详尽配置实战(for spring)
一. 序
在实际项目中使用Hibernate有两年多了,在两年多的实践过程中既体验到了Hibernate带来的N多好处,同时也碰到不少的问题,特写此篇文章做个总结,记录自己在Hibernate实践中的一些经验,希望对于新使用Hibernate的朋友能有个帮助,避免走过多的弯路。
阅读本文前建议至少拥有Hibernate的一些基本知识,因为本文不会去详细介绍相关的基本知识,最好就是先用Hibernate开发了一个HelloWorld,^_^。
根据自己所经历的项目中使用Hibernate所涉及的范围,本文从开发环境、开发、设计、性能、测试以及推荐的相关书籍方面进行讲述,本篇文档不会讲的非常细致,只是根据自己在实践时的经验提出一些建议,关于细致以及具体的部分请参阅《Hibernate Reference》或推荐的相关书籍章节。
此文档的PDF版本请到此下载:
http://www.blogjava.net/Files/BlueDavy/Hibernate 实践.rar
本文允许转载,但转载时请注明作者以及来源。
作者:BlueDavy
来源:www.blogjava.net/BlueDavy
二. 开发环境
Hibernate 开发环境的搭建非常的简单,不过为了提高基于Hibernate开发的效率,通常都需要使用一些辅助工具,如xdoclet、middlegen等。
尽管Hibernate已经封装提供了很简单的进行持久的方法,但在实际项目的使用中基本还是要提供一些通用的代码,以便在进行持久的相关操作的时候能够更加的方便。
2.1. lib
2.1.1. Hibernate lib
Hibernate 相关的 lib 自然是开发环境中首要的问题,这部分可以从 Hibernate 的官方网站进行下载,在其官方网站中同时提供了对于 Hibernate 所必须依赖的 lib 以及其他可选 lib 的介绍。
2.2. xdoclet
Hibernate 作为ORM工具,从名字上就能看出它需要一个从O à R 的Mapping的描述,而这个描述就是在使用Hibernate时常见的hbm.xml,在没有工具支持的情况下,需要在编写持久层对象的同时手写这个文件,甚为不便。
在jdk 5.0未推出之前,xdoclet支持的在javadoc中编写注释生成相关配置文件的方式大受欢迎,减少了编写hibernate映射文件的复杂性,手写一个完整的hibernate映射文件出错几率比较的高,再加上手写容易造成编写出来的风格相差很大,因此,基于xdoclet来生成hbm.xml的方式被大量的采用,基于xdoclet来编写能够基于我们在持久层对象上编写的javadoc来生成hbm.xml文件,非常的方便。
2.2.1. Hibernate template
如果没记错的话,大概在 04 年的时候 javaeye 上有位同仁整理了一个这样的 template 文件, ^_^ ,非常感谢,我一直都在用着,呵呵。
这个文件的方便就是把它导入 eclipse 后,在 javadoc 中我们可以直接写 hibid ,然后按 eclipse 的代码辅助键 (alt+/) 来生成整个 hibernate.id 的相关的格式,呵呵,免得在写 hibernate.id 这些东西的时候过于麻烦, ^_^ ,这个 template 文件我稍微做了修改,可在这里下载:
http://www.blogjava.net/Files/BlueDavy/templates-eclipse-tags.rar
当然,你也可以选择直接用 xdoclet 提供的 template 文件,不过 xdoclet 官方网站上好像只提供了可直接导入 idea 的模板文件。
关于注释上的 hibernate.id 这些东西具体请参见 xdoclet 官方网站的说明。
如果你的项目采用的是 jdk 5 ,那么就可以直接使用 hibernate annotation 了,那就更为方便。
2.2.2. Ant task build
Eclipse 里没有集成 xdoclet 的插件,你也可以去安装一个 jboss ide 的插件,里面有 xdoclet 的插件,反正我是觉得太麻烦了。
在项目中我仍然采用 ant task 的方式来生成 hbm.xml , target 如下所示:
<path id="app.classpath">
<pathelement path="${java.class.path}"/>
<fileset dir="${xdoclib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="hbm" description=" 生成映射文件 ">
<tstamp>
<format property="TODAY" pattern="yy-MM-dd"/>
</tstamp>
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.HibernateDocletTask" classpathref="app.classpath"/>
<hibernatedoclet destdir="src/java" force="true" verbose="true" excludedtags="@version,@author,@todo">
<fileset dir="src/java">
<include name="**/po/**/*.java"/>
</fileset>
<hibernate version ="3.0"/>
</hibernatedoclet>
</target>
这个文件请根据项目情况以及环境稍做修改, ^_^ ,其中需要通过 properties 文件指明 xdocletlib.dir ,类似 xdocletlib.dir=c:\xdocletlib ,里面放置 xdoclet 的相关 jar 文件。
在搭建好了这样的环境后,就可以在直接在 eclipse 中运行 ant 文件中的这个 target 来生成 hbm.xml 。
2.3. Hibernate3 Tools
如果采用Hibernate 3,则可以直接下载Hibernate 3 Tools的Eclipse Plugin,那就可以类似在PL/SQL里执行sql一样在eclipse里执行hql,^_^
2.4. HibernateUtil
为了方便项目中Hibernate的使用,一般来说都会提供HibernateUtil这样的类,这个类的作用主要是创建sessionFactory和管理session,在Hibernate 3以前采用的是在这里建立ThreadLocal来存放session,在Hibernate 3以后则可以直接使用SessionFactory.getCurrentSession来获取session,而session的获取方式则可通过在hibernate.cfg.xml中执行current_session_context_class的属性来决定是采用thread或jta或自定义的方式来产生session。
2.5. CommonDao
在持久层部分目前采用的较多的仍然是dao模式,Hibernate作为ORM工具已经提供了CRUD的封装,类如可以使用session.save();session.persist()这样简单的方式来完成CRUD的操作,但在实际的项目中还是需要提供一个通用的Dao,来简化对于事务、异常处理以及session的操作,同时提供一些项目中需要的相关操作。
三. 开发
在完成了Hibernate的开发环境的搭建后,就可以基于Hibernate进行持久层的开发了,对于持久层开发来说,会涉及到实体的编写、实体的维护以及实体的查询三个部分。
3.1. 实体的编写
Hibernate 的一个明显的优点就是在于可透明化的对对象进行持久,这也就意味着持久对象根本就不需要依赖任何的东西,可以采用POJO的方式来编写,在Hibernate 3以上版本还提供了对于Map、XML的方式的持久的支持,就更方便了,在项目中,更多采用的仍然是POJO的方式。
在实体的编写上应该说不会有什么问题,只要仔细查看xdoclet关于hibernatedoclet部分的说明即可完成。
这块需要学习的主要是普通的值类型注释的编写、id字段注释的编写、关联注释的编写,这些部分xdoclet均提供了较详细的说明。
3.2. 实体的维护
3.2.1. 新增 / 编辑 / 删除
新增 / 编辑 / 删除是持久操作中最常使用的维护性操作,基于 Hibernate 做这样的维护就比采用 sql 的方式简单多了,通过上面 CommonDao ,就可以直接完成 dao.save 、 dao.update 、 dao.delete 的操作,而且在 Hibernate 3 也支持了批量的 insert 、 update 和 delete 。
这个部分中需要注意的是 Hibernate 对于对象的三种状态的定义:
u Transient
很容易理解,就是从未与 session 发生过关系的对象, ^_^ ,例如在代码中直接 User user=new User() ;这样形成的 user 对象,就称为 Transient 对象了。
u Detached
同样很容易理解,就是与 session 发生过关系的对象,但 session 已经关闭了的情况下存在的对象,例如:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
session.close();
在 session.close() 后这个时候的 user 对象就处于 Detached 状态之中了,如果想将这个对象变为 Persistent 状态,可以通过 session.merge 或 session.saveOrUpdate() 等方式来实现。
Detached 状态的对象在实际的应用中最常采用,从概念上我们可以这么理解,处于 Detached 状态的对象可以看做是一个 DTO ,而不是 PO ,这从很大程度上就方便了 PO 在实际项目中的使用了。
u Persistent
Persistent 状态就是指和 Session 发生了关系的对象,并且此时 session 未关闭,举例如下:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
user.getName();
在 session.save 后 user 就处于 Persistent 状态,此时如果通过 session 根据 user 的 id 去获取 user 对象,则可发现获取的对象和之前的 user 是同一个对象,这是 session 一级缓存所起的作用了,当然,也可以强制的刷新 session 的一级缓存,让 session 从数据库中重新获取,只需要在获取前执行 session.evict(user) 或 session.clear() 。
3.2.2.关联维护
关联维护在 Hibernate 中表现出来可能会让熟悉使用 sql 的人有些的不熟,但其实以对象的观点去看是会觉得很正常的。
在 Hibernate 的关联维护中,最重要的是 inverse 和 cascade 两个概念。
u inverse
inverse 从词义上看过去可能不是那么容易理解,其实它的意思就是由谁来控制关联关系的自动维护,当 inverse=true 就意味着当前对象是不能自动维护关联关系,当 inverse=false 就意味着当前对象可自动维护关联关系,还是举例来说:
假设 Org 和 User 一对多关联,
当 org 中 getUsers 的 inverse=false 的情况:
org.getUsers().add(user);
dao.save(org);
这样执行后将会看到数据库中 user 这条记录中的 orgId 已经被设置上去了。
当 inverse=true 的情况下,执行上面的代码,会发现在数据库中 user 这条记录中的 orgId 没有被设置上去。
^_^ , inverse 的作用这样可能看的不是很明显,在下面的一对多中会加以描述。
u cascade
cascade 的概念和数据库的 cascade 概念是基本一致的, cascade 的意思形象的来说就是当当前对象执行某操作的情况下,其关联的对象也执行 cascade 设置的同样的操作。
例如当 org.getUsers 的 cascade 设置为 delete 时,当删除 org 时,相应的 users 也同样被删除了,但这个时候要注意, org.getUsers 这个集合是被删除的 user 的集合,也就是说如果这个时候数据库中新增加了一个 user 给 org ,那么这个 user 是不会被删除的。
cascade 的属性值详细见《 Hibernate reference 》。
3.2.2.1.一对一
一对一的关联维护在实际项目中使用不多,一对一在Hibernate中可采用两种方式来构成,一种是主键关联,一种是外键关联。
一对一的使用推荐使用主键关联,具体配置方法请参见《Hibernate Reference》。
3.2.2.2. 一对多/多对一
一对多/多对一的关联维护在实际项目中使用是比较多的,在Hibernate中可采用多种方式来配置一对多的关联,如采用Set、List、Bag、Map等,具体在《Hibernate Reference》中都有详细说明。
在这里我想说的一点就是关于inverse的设置,在一对多的情况下建议将一端的inverse设为true,而由多端去自动维护关联关系,为什么这样做其实挺容易理解的,假设org和user为一对多的关联,org.getUsers的inverse设置为false,org.getUsers().add(user);dao.update(org);当update的时候org所关联的所有user的orgId都会更新一次,可想而知这个效率,而如果改为在多端维护(多端设置为inverse=false),则是这样:user.setOrg(org);dao.update(user);当update的时候就仅仅是更新user这一条记录而已。
另外一点就是合理的设置cascade,这个要根据需求来实际决定。
3.2.2.3.多对多
多对多的关联维护在实际项目中其实也是比较多的,尽管在《Hibernate Reference》中认为多对多的情况其实很多时候都是设计造成的。
多对多的关联也同样可以采用Set、List等多种方式来配置,具体在《Hibernate Reference》中也有详细的说明。
多对多的关联维护上没有什么需要多说的,在实践过程中来看这块不会出什么太多问题,唯一需要注意的是合理设置cascade,这个要根据项目的实际情况而定。
3.3. 实体的查询
Hibernate 提供了多种方式来支持实体的查询,如对于原有熟悉sql的人可以继续使用sql,符合对象语言的对象查询语句(HQL)以及条件查询API(Criteria)。
在熟练使用hql或criteria的情况下,我相信你会觉得Hibernate的查询方式会比采用sql的方式更加简便。
3.3.1.符合对象语言的查询语句
Hibernate 提供了一种符合对象语言的查询语句,称为 HQL ,这种语句的好处是能够避免使用 sql 的情况下依赖数据库特征的情况出现,同时它带来的最大的好处就是我们能够根据 OO 的习惯去进行实体的查询。
对于 HQL 没有什么多讲的,如果熟悉 sql 的人应该也是能够很快就学会 HQL ,而如果不熟悉 sql 的人那也没关系, HQL 的上手是非常容易的,具体请参考《 Hibernate Reference 》。
3.3.2.占位符式的查询
占位符式的查询 ( 就是采用 ? 替换查询语句中的变量 ) 是在采用 sql 的情况下经常使用的一种查询方式,也是查询时推荐使用的一种方式。
Hibernate 中的查询参数主要有两种类型:值类型和实体类型,值类型就是指一个切实的值 ( 如 String 、 int 、 List 这些 ) ,实体类型就是一个具体的实体,如编写的 User 、 Organization 等,值类型的查询和普通 sql 几乎一样,而实体类型的查询就体现了 Hibernate 的强项, ^_^ ,可以起到简化 sql 的作用,并且使得查询语句更加容易理解。
3.3.2.1.值类型
3.3.2.1.1.简单值
举例如下:
from User u where u.name=:username and u.yearold=:yearold
这就是一个常见的简单值的占位符式的查询,通过这样的方式就可以把值注入到参数中:
query.setParameter(“username”,”bluedavy”);
query.setParameter(“yearold”,25);
同样, hibernate 也支持和 sql 完全相同的 ? 的方式,那么上面的语句以及注入参数的方式就变为了:
from User u where u.name=? and u.yearold=?
query.setParameter(0,”bluedavy”);
query.setParameter(1,25);
推荐使用第一种,那样参数的意义更容易被理解。
3.3.2.1.2. in 查询
in 查询也是经常被使用到的一种查询,在 Hibernate 中表现出来会稍有不同,不过如果按照对象观点去看就很容易理解了,例如下面这句:
from User u where u.name in (:usernameList)
在 Hibernate 中通过这样的方式将值注入到这个参数中:
List list=new ArrayList();
list.add(“jerry”);
list.add(“bluedavy”);
query.setParameterList(“usernameList”,list);
在 sql 中通常是组装一个由 , 连接的值来构成 in 中的参数值,而在 Hibernate 中则依照对象转化为采用 list 了, ^_^ ,是不是更方便些。
3.3.2.2.实体类型
在Hibernate中关联采用的都是对象形式,表现对外就是隐藏了数据库的外键的部分,这也就对习惯使用sql查询的人带来一个问题,因为无法再操作外键字段,那么在涉及到关联的实体的查询时应该怎么做呢,我把它分为单实体和实体集合两种情况来说说。
3.3.2.2.1.单实体
单实体的查询对应到 sql 情况通常是在一对多的情况下通过多端查询同时结合一端的一些过滤条件,在 sql 中通常采用 join 的方式来实现这个,而在 Hibernate 中要实现这点就更容易了,举例如下:
User 和 Organization 是一对多,现在要查询属于组织机构名称为 ”Blogjava” 以及用户年龄大于 20 的用户:
from User u where u.org.name=:orgname and u.yearold>:yearold
query.setParameter(“orgname”,”Blogjava”);
query.setParameter(“yearold”,20);
可以看到这样的查询语句比 sql 更简单多了,同时也更容易理解多了。
3.3.2.2.2.实体集合
实体集合过滤形式的查询在实际的项目中也经常会碰到,仍然用上面的例子,但改为通过 Organization 去查询:
from Organization org where org.name=:orgname and org.users.yearold>:yearold
是不是比 sql 简单多了,而且更容易理解呢, ^_^
这个时候对象化查询语句的优势就体现出来了,而不用陷入 sql 的那种关系型的通过外键进行查询的方式。
3.3.3.NamedQuery
NamedQuery 的意思就是指在 PO 的映射文件中定义关于 PO 的查询语句,而在应用中指需要直接调用此查询语句的别名即可,这个好处非常明显,使得所有的查询语句可以统一的进行管理,同样,我们可以在 PO 中通过 javadoc 的方式进行定义,这就更方便了, ^_^
操作 NamedQuery 的方法和普通 hql 的方法基本一样:
session.getNamedQuery(queryname);
其中的 queryname 就是我们定义的查询语句的别名,一个 namedQuery 的语句的示例如下:
< query name = "validate" ><![CDATA[
from User u where u.loginname=:loginname and u.password=:password
]]></ query >
3.3.4. Criteria
条件查询的 API 使得我们可以采用完全对象化的方式进行实体的查询,而不是通过 hql 的方式,在实际项目中,使用 hql 的方式更为居多,毕竟写起来更方便。
关于 Criteria 的具体介绍请参阅《 Hibernate Reference 》。
3.3.5.原生 SQL
原生 SQL 不推荐使用,但在某些确实需要用 sql 的情况下那么 Hibernate 还是支持的,具体见《 Hibernate Reference 》。
四.设计
独立的编写这个章节的原因是希望在采用Hibernate的情况下充分的去发挥Hibernate的优势,改变我们以关系形式去做持久层的设计的惯性思维,形成以OO的思想去设计持久层,所以我非常推荐通过写PO去生成数据表的方式,而不是设计表反向导成PO的形式,当然,对于原有的系统那就没办法了。
OO 思想中的核心三要素:封装、继承和多态,在Hibernate的支持下同样可以充分发挥OO的三要素来优化持久层的设计。
4.1. 封装
4.1.1. Component
Hibernate 中有一个 Component 的概念,这就允许在进行持久层设计的时候采用细粒度级的领域模型进行设计,例如在 User 对象中需要记录 User 的 firstname 、 lastname 这些信息,而在其他的表中也有这种需求,那么在 Hibernate 中我们就可以把 firstname 、 lastname 组装为一个 UserName 对象,作为 Component 放入 User 中,在 user 中就可以变为采用 user.getUserName.getFristName 的方式来获取。
Component 对于我们采用对象的封装概念进行持久层设计提供了很好的支持,同时在 Hibernate 中还有 Elements 、 Properties 这些元素,具体请参见《 Hibernate Reference 》。
4.2. 继承
继承使得我们可以对持久层中的对象进行抽象,类如我们可以形成Person这个对象,而User、Employee都继承自这个对象。
继承在数据库形式的设计中固然也可以实现,但通常不能以对象的观点去发挥的淋漓尽致,当然不是说以对象的方式去设计一定是最好的。
在Hibernate中对于继承映射到数据表有几种不同的策略,各有适用的不同场合,具体的解释和说明见《Hibernate Reference》
4.2.1.单表策略
单表策略很容易理解,就是将类、子类中所有的属性都放至一张表中,这对于子类属性不多的情况非常有效。
在 Hibernate 中通常将子类定义为 @hibernate.subclass 的方式来实现这个策略。
4.2.2.每个子类一张表
每个子类一张表在 Hibernate 中有几种实现方式, @hibernate.join-subclass 、 @hibernate.join-subclass-key 的组合方式以及 @hibernate.join-subclass 、 @hibernate.discriminator 的组合方式是较常用的两种方式,第一种方式采用的是主键关联方式,第二种方式采用的是 discriminator 字段的关联方式,个人比较推崇第一种方式。
这种策略适合在子类属性和父类有较大不同的情况下采用。
4.2.3.每个具体类一张表
这种策略适合在类层次结构上有一定数量的抽象类的情况下使用,同样有两种方式,一种是采用显式多态的方式,另一种是采用隐式多态的方式,显式多态采用的为 @hibernate.union-subclass 的方式,隐式多态则采用每个具体类的 PO 独立建表的策略,在它的映射文件中将看不出任何的和接口、抽象类的关系,同时对于抽象类,需要指明其 abstract=”true” 。
4.3. 多态
4.3.1.查询
在查询中很容易体现 Hibernate 对于多态的支持,如系统有 Person 对象、 User 和 Employee 分别继承自 Person ,同时 Person 和 Organization 对象关联,这个时候我们通过 Organization 获取其关联的 Person 时得到的既有可能是 User ,也有可能是 Employee , ^_^…
五.性能
Hibernate 作为ORM工具,从性能上来讲带给了很多人忧虑,但我觉得Hibernate在性能上也许会带来少许的降低,但如果对于不能合理设计数据库和使用SQL的人来说,我觉得Hibernate反倒能提高性能,除非是在一些特殊的场合,如报表式的那种查询推荐继续采用JDBC的方式。
Hibernate 在性能提升上其实有很多种做法,在《Hibernate Reference》中也有专门的提升性能的章节,在这里我提几点在项目中通常采用的方法。
5.1. Lazy Load
Lazy Load 是常用的一种提升性能的方法,这个其实很容易理解,在不采用lazy load的情况下,Hibernate在获取一个PO的时候,将同时获取PO中的属性、PO中的集合以及集合中对象的属性、集合,这样看过去很容易看出,如果对象的关联结构有深层次的话,最后搞不好整个库都被加载出来了,而在实际使用中往往可能只需要用到PO中的一两个属性而已,这点也是之前的ORM产品经常被批的一点,就是ORM产品不能象sql那样只获取需要的东西,^_^,其实Hibernate在这点上一直就支持,而且支持的还不错,在Hibernate 3以后,默认的lazy就已经设置为true了,这个时候包括po中的属性都是采用lazy load的方式,只有在调用到这个属性时才会从缓存或数据库中加载,当然,集合也同样如此。
在lazy load上推荐不要什么字段都采用lazy load的方式,对于一些基本属性的字段建议将其lazy设置为false,而对于一些可能需要消耗内存的字段,如clob这样的字段对象的lazy设置为true,对于集合则全部设置为lazy=true。
是否采用Lazy load对系统的性能会有非常明显的影响,同时尽量不要将Detached Object放入Http的session中。
5.1.1.OSIV
OSIV : Open Session In View ,在 B/S 系统中通常采用这种方式来更好的去支持 Lazy load ,意思就是在 View 加载前打开 Session ,在 View 加载完毕后关闭 Session 的方式,在 Spring 中有 OpenSessionInViewFilter ,可参考或直接使用。
5.2. Cache
Cache 是在提升系统性能方面常用的方法,在Hibernate中通常有非常好的对于Cache的支持方法,Hibernate中对于Cache有一级缓存和二级缓存的概念,一级缓存是必须的,位于Session部分,二级缓存则不是必须的,由开发人员自行指定,二级缓存可指定使用何种开源的cache工具,Hibernate 3以后的版本默认使用的是Ehcache,也可以切换为Oscache、JbossCache,对我而言最重要的区别在于对于cluster的支持上。
二级缓存能够明显的提高系统的性能,当然,同时也会更加的消耗内存,可以通过配置文件来指定内存中能够加载的最多的元素,这有利于避免消耗过多内存。
二级缓存的设置在Hibernate中非常的简单,只需要在相应的hbm.xml中增加cache元素,指明使用何种策略,如read-only、read-write等,也可以直接在hibernate.cfg.xml中增加class-cache的方式来进行全局指定。
5.3. 高效的查询语句
查询语句的是否高效对于系统的性能也是会造成明显的影响的,为了方便系统性能的调优,建议大家对查询语句进行统一管理,如统一采用NamedQuery的方式,在这样的情况下可以在系统运行时请教数据库专家,由他们来分析系统中的查询语句的执行效率以及提出改进策略,而对于开发人员来讲,在查询语句方面最能够注意的就是采用占位符式的查询。
5.3.1.占位符式的查询
数据库对于所有的 sql 语句都要进行语法分析,而其分析通常会受到语句中的大小写、空格以及参数不同的影响,在其语法分析器认为不同的情况下将再次进行分析,这就不可避免的降低了响应的速度,而采用占位符式的查询则可保证语法分析器只进行一次的分析,在参数不同的情况并不会出现重复解析的现象,其次就是要统一查询语句的编写风格,包括大小写、空格这些。
我不是很确定 Hibernate 中对于语句的语法分析,估计和数据库的这种方式应该差不多,不过猜想可能会更智能一些, ^_^
5.4. 一些配置
在 hibernate.cfg.xml 中的一些配置也会对性能产生一定的影响,如 jdbc.fetch_size 的设置等,还有象采用连接池方面的设置,对于 B/S 应用的情况建议尽量采用应用服务器提供的 JNDI 的方式。
5.5. 建议
在性能提升方面从两方面入手,一是持久层对象的设计上,这方面可以参考《 Hibernate Reference 》中提升性能章节中的一些建议,另一方面则是请教数据库专家,由数据库专家对表结构、查询语句等进行分析来给出改进策略,在现有的一个项目中,竟然有出现 Hibernate 在外键上没建立索引的现象出现?
六. 测试
6.1. 编写专门的测试用的配置文件
测试方面也是极度关心的话题,在测试方面其实比较简单,只需要在测试类中采用专门用于测试的配置文件即可,在这个配置文件中,通过都是采用设置hbm2ddl.auto属性为create-drop的方式,也就是在测试类运行前创建表,在测试类运行后删除表的策略,在更多的情况下,我们可以采用in-memory的数据库的方式,如hsql,当然,有些时候则需要和实际运行环境一致,那么就需要采用建立专门的测试库的方式,避免测试数据和运行数据的相互影响。
七. 企业应用开发
事务和并发是企业应用开发中非常关注的两个话题,在《Hibernate Reference》中提供了详细的方案,在这里我就简单的说说。
7.1. 事务
事务是企业应用开发中非常重视的一点,而在Hibernate中操作此部分和sql方式没有什么很大的区别,可以通过session主动去获取Transaction来实现事务控制,同时也可以交由应用服务器提供的JTA来实现事务控制。
在事务这个级别上如果有更高的要求,建议采用Spring的事务框架。
7.2. 并发
在并发方面多采用锁策略,锁策略和数据库基本相同,同样是乐观锁和悲观锁两种策略,乐观锁策略在Hibernate中推荐使用version或timestamp来实现,具体覆盖方式则需要根据应用而定,如是采用最新的修改的覆盖还是采用版本冲突策略等,悲观锁策略则通过指定对象的锁方式,如LockMode.READ,引用《Hibernate Reference》中的一段话:
“用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定,或者在一个新的事务启动的时候,重新进行锁定。Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!
如果数据库不支持用户设置的锁定模式,Hibernate将使用适当的替代模式,这一点可以确保应用程序的可移植性。”。
用户可通过几种方式来指定锁定模式:
u Session.load() 的时候指定锁定模式LockMode;
u Session.lock() ;
u Query.setLockMode() 。
八. 相关书籍
Hibernate 上手并不难,但要真正的用好它确实不是件容易的事,有些书籍能够很好的帮我们快速的提供解决思路和解决方案,而这些书籍我们也应该常备,以方便自己在有些问题上的解答。
同时,我一直坚持的观点,一种开源框架通常带来的不仅仅是开发、使用上的改变,带来的最大的改变仍然是在设计层次上的,设计上能否充分的发挥开源框架的优势才是最为重要的。
8.1. 《Hibernate Reference》
这本没什么说的,必读书籍,也许在读的时候很多东西你不会觉得什么,但当碰到一些确定方向的问题时,可以通过此书快速的查找到相应的解决方案,感谢Redsaga组织的翻译工作,使得我们可以有中文版可看。
目前版本(Hibernate 3.1.2)的下载地址:
http://www.redsaga.com/hibernate-ref/3.1.2/zh-cn/pdf/hibernate_reference.pdf
8.2. 《Hibernate in action》
In action 系列的书籍也没啥多说的,强烈推荐看看,尽管现在看起来版本有些老了,但里面很多的实践思想仍然是非常值得学习的,网上应该有很多电子版下载的地方。
8.3. 《深入浅出Hibernate》
这本书想必大家也听闻了不少,简称白皮书,^_^,是夏昕、曹晓刚以及唐勇三位大师的大作。
在实际项目中使用Hibernate有两年多了,在两年多的实践过程中既体验到了Hibernate带来的N多好处,同时也碰到不少的问题,特写此篇文章做个总结,记录自己在Hibernate实践中的一些经验,希望对于新使用Hibernate的朋友能有个帮助,避免走过多的弯路。
阅读本文前建议至少拥有Hibernate的一些基本知识,因为本文不会去详细介绍相关的基本知识,最好就是先用Hibernate开发了一个HelloWorld,^_^。
根据自己所经历的项目中使用Hibernate所涉及的范围,本文从开发环境、开发、设计、性能、测试以及推荐的相关书籍方面进行讲述,本篇文档不会讲的非常细致,只是根据自己在实践时的经验提出一些建议,关于细致以及具体的部分请参阅《Hibernate Reference》或推荐的相关书籍章节。
此文档的PDF版本请到此下载:
http://www.blogjava.net/Files/BlueDavy/Hibernate 实践.rar
本文允许转载,但转载时请注明作者以及来源。
作者:BlueDavy
来源:www.blogjava.net/BlueDavy
二. 开发环境
Hibernate 开发环境的搭建非常的简单,不过为了提高基于Hibernate开发的效率,通常都需要使用一些辅助工具,如xdoclet、middlegen等。
尽管Hibernate已经封装提供了很简单的进行持久的方法,但在实际项目的使用中基本还是要提供一些通用的代码,以便在进行持久的相关操作的时候能够更加的方便。
2.1. lib
2.1.1. Hibernate lib
Hibernate 相关的 lib 自然是开发环境中首要的问题,这部分可以从 Hibernate 的官方网站进行下载,在其官方网站中同时提供了对于 Hibernate 所必须依赖的 lib 以及其他可选 lib 的介绍。
2.2. xdoclet
Hibernate 作为ORM工具,从名字上就能看出它需要一个从O à R 的Mapping的描述,而这个描述就是在使用Hibernate时常见的hbm.xml,在没有工具支持的情况下,需要在编写持久层对象的同时手写这个文件,甚为不便。
在jdk 5.0未推出之前,xdoclet支持的在javadoc中编写注释生成相关配置文件的方式大受欢迎,减少了编写hibernate映射文件的复杂性,手写一个完整的hibernate映射文件出错几率比较的高,再加上手写容易造成编写出来的风格相差很大,因此,基于xdoclet来生成hbm.xml的方式被大量的采用,基于xdoclet来编写能够基于我们在持久层对象上编写的javadoc来生成hbm.xml文件,非常的方便。
2.2.1. Hibernate template
如果没记错的话,大概在 04 年的时候 javaeye 上有位同仁整理了一个这样的 template 文件, ^_^ ,非常感谢,我一直都在用着,呵呵。
这个文件的方便就是把它导入 eclipse 后,在 javadoc 中我们可以直接写 hibid ,然后按 eclipse 的代码辅助键 (alt+/) 来生成整个 hibernate.id 的相关的格式,呵呵,免得在写 hibernate.id 这些东西的时候过于麻烦, ^_^ ,这个 template 文件我稍微做了修改,可在这里下载:
http://www.blogjava.net/Files/BlueDavy/templates-eclipse-tags.rar
当然,你也可以选择直接用 xdoclet 提供的 template 文件,不过 xdoclet 官方网站上好像只提供了可直接导入 idea 的模板文件。
关于注释上的 hibernate.id 这些东西具体请参见 xdoclet 官方网站的说明。
如果你的项目采用的是 jdk 5 ,那么就可以直接使用 hibernate annotation 了,那就更为方便。
2.2.2. Ant task build
Eclipse 里没有集成 xdoclet 的插件,你也可以去安装一个 jboss ide 的插件,里面有 xdoclet 的插件,反正我是觉得太麻烦了。
在项目中我仍然采用 ant task 的方式来生成 hbm.xml , target 如下所示:
<path id="app.classpath">
<pathelement path="${java.class.path}"/>
<fileset dir="${xdoclib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="hbm" description=" 生成映射文件 ">
<tstamp>
<format property="TODAY" pattern="yy-MM-dd"/>
</tstamp>
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.HibernateDocletTask" classpathref="app.classpath"/>
<hibernatedoclet destdir="src/java" force="true" verbose="true" excludedtags="@version,@author,@todo">
<fileset dir="src/java">
<include name="**/po/**/*.java"/>
</fileset>
<hibernate version ="3.0"/>
</hibernatedoclet>
</target>
这个文件请根据项目情况以及环境稍做修改, ^_^ ,其中需要通过 properties 文件指明 xdocletlib.dir ,类似 xdocletlib.dir=c:\xdocletlib ,里面放置 xdoclet 的相关 jar 文件。
在搭建好了这样的环境后,就可以在直接在 eclipse 中运行 ant 文件中的这个 target 来生成 hbm.xml 。
2.3. Hibernate3 Tools
如果采用Hibernate 3,则可以直接下载Hibernate 3 Tools的Eclipse Plugin,那就可以类似在PL/SQL里执行sql一样在eclipse里执行hql,^_^
2.4. HibernateUtil
为了方便项目中Hibernate的使用,一般来说都会提供HibernateUtil这样的类,这个类的作用主要是创建sessionFactory和管理session,在Hibernate 3以前采用的是在这里建立ThreadLocal来存放session,在Hibernate 3以后则可以直接使用SessionFactory.getCurrentSession来获取session,而session的获取方式则可通过在hibernate.cfg.xml中执行current_session_context_class的属性来决定是采用thread或jta或自定义的方式来产生session。
2.5. CommonDao
在持久层部分目前采用的较多的仍然是dao模式,Hibernate作为ORM工具已经提供了CRUD的封装,类如可以使用session.save();session.persist()这样简单的方式来完成CRUD的操作,但在实际的项目中还是需要提供一个通用的Dao,来简化对于事务、异常处理以及session的操作,同时提供一些项目中需要的相关操作。
三. 开发
在完成了Hibernate的开发环境的搭建后,就可以基于Hibernate进行持久层的开发了,对于持久层开发来说,会涉及到实体的编写、实体的维护以及实体的查询三个部分。
3.1. 实体的编写
Hibernate 的一个明显的优点就是在于可透明化的对对象进行持久,这也就意味着持久对象根本就不需要依赖任何的东西,可以采用POJO的方式来编写,在Hibernate 3以上版本还提供了对于Map、XML的方式的持久的支持,就更方便了,在项目中,更多采用的仍然是POJO的方式。
在实体的编写上应该说不会有什么问题,只要仔细查看xdoclet关于hibernatedoclet部分的说明即可完成。
这块需要学习的主要是普通的值类型注释的编写、id字段注释的编写、关联注释的编写,这些部分xdoclet均提供了较详细的说明。
3.2. 实体的维护
3.2.1. 新增 / 编辑 / 删除
新增 / 编辑 / 删除是持久操作中最常使用的维护性操作,基于 Hibernate 做这样的维护就比采用 sql 的方式简单多了,通过上面 CommonDao ,就可以直接完成 dao.save 、 dao.update 、 dao.delete 的操作,而且在 Hibernate 3 也支持了批量的 insert 、 update 和 delete 。
这个部分中需要注意的是 Hibernate 对于对象的三种状态的定义:
u Transient
很容易理解,就是从未与 session 发生过关系的对象, ^_^ ,例如在代码中直接 User user=new User() ;这样形成的 user 对象,就称为 Transient 对象了。
u Detached
同样很容易理解,就是与 session 发生过关系的对象,但 session 已经关闭了的情况下存在的对象,例如:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
session.close();
在 session.close() 后这个时候的 user 对象就处于 Detached 状态之中了,如果想将这个对象变为 Persistent 状态,可以通过 session.merge 或 session.saveOrUpdate() 等方式来实现。
Detached 状态的对象在实际的应用中最常采用,从概念上我们可以这么理解,处于 Detached 状态的对象可以看做是一个 DTO ,而不是 PO ,这从很大程度上就方便了 PO 在实际项目中的使用了。
u Persistent
Persistent 状态就是指和 Session 发生了关系的对象,并且此时 session 未关闭,举例如下:
User user=new User();
user.setName(“bluedavy”);
session.save(user);
user.getName();
在 session.save 后 user 就处于 Persistent 状态,此时如果通过 session 根据 user 的 id 去获取 user 对象,则可发现获取的对象和之前的 user 是同一个对象,这是 session 一级缓存所起的作用了,当然,也可以强制的刷新 session 的一级缓存,让 session 从数据库中重新获取,只需要在获取前执行 session.evict(user) 或 session.clear() 。
3.2.2.关联维护
关联维护在 Hibernate 中表现出来可能会让熟悉使用 sql 的人有些的不熟,但其实以对象的观点去看是会觉得很正常的。
在 Hibernate 的关联维护中,最重要的是 inverse 和 cascade 两个概念。
u inverse
inverse 从词义上看过去可能不是那么容易理解,其实它的意思就是由谁来控制关联关系的自动维护,当 inverse=true 就意味着当前对象是不能自动维护关联关系,当 inverse=false 就意味着当前对象可自动维护关联关系,还是举例来说:
假设 Org 和 User 一对多关联,
当 org 中 getUsers 的 inverse=false 的情况:
org.getUsers().add(user);
dao.save(org);
这样执行后将会看到数据库中 user 这条记录中的 orgId 已经被设置上去了。
当 inverse=true 的情况下,执行上面的代码,会发现在数据库中 user 这条记录中的 orgId 没有被设置上去。
^_^ , inverse 的作用这样可能看的不是很明显,在下面的一对多中会加以描述。
u cascade
cascade 的概念和数据库的 cascade 概念是基本一致的, cascade 的意思形象的来说就是当当前对象执行某操作的情况下,其关联的对象也执行 cascade 设置的同样的操作。
例如当 org.getUsers 的 cascade 设置为 delete 时,当删除 org 时,相应的 users 也同样被删除了,但这个时候要注意, org.getUsers 这个集合是被删除的 user 的集合,也就是说如果这个时候数据库中新增加了一个 user 给 org ,那么这个 user 是不会被删除的。
cascade 的属性值详细见《 Hibernate reference 》。
3.2.2.1.一对一
一对一的关联维护在实际项目中使用不多,一对一在Hibernate中可采用两种方式来构成,一种是主键关联,一种是外键关联。
一对一的使用推荐使用主键关联,具体配置方法请参见《Hibernate Reference》。
3.2.2.2. 一对多/多对一
一对多/多对一的关联维护在实际项目中使用是比较多的,在Hibernate中可采用多种方式来配置一对多的关联,如采用Set、List、Bag、Map等,具体在《Hibernate Reference》中都有详细说明。
在这里我想说的一点就是关于inverse的设置,在一对多的情况下建议将一端的inverse设为true,而由多端去自动维护关联关系,为什么这样做其实挺容易理解的,假设org和user为一对多的关联,org.getUsers的inverse设置为false,org.getUsers().add(user);dao.update(org);当update的时候org所关联的所有user的orgId都会更新一次,可想而知这个效率,而如果改为在多端维护(多端设置为inverse=false),则是这样:user.setOrg(org);dao.update(user);当update的时候就仅仅是更新user这一条记录而已。
另外一点就是合理的设置cascade,这个要根据需求来实际决定。
3.2.2.3.多对多
多对多的关联维护在实际项目中其实也是比较多的,尽管在《Hibernate Reference》中认为多对多的情况其实很多时候都是设计造成的。
多对多的关联也同样可以采用Set、List等多种方式来配置,具体在《Hibernate Reference》中也有详细的说明。
多对多的关联维护上没有什么需要多说的,在实践过程中来看这块不会出什么太多问题,唯一需要注意的是合理设置cascade,这个要根据项目的实际情况而定。
3.3. 实体的查询
Hibernate 提供了多种方式来支持实体的查询,如对于原有熟悉sql的人可以继续使用sql,符合对象语言的对象查询语句(HQL)以及条件查询API(Criteria)。
在熟练使用hql或criteria的情况下,我相信你会觉得Hibernate的查询方式会比采用sql的方式更加简便。
3.3.1.符合对象语言的查询语句
Hibernate 提供了一种符合对象语言的查询语句,称为 HQL ,这种语句的好处是能够避免使用 sql 的情况下依赖数据库特征的情况出现,同时它带来的最大的好处就是我们能够根据 OO 的习惯去进行实体的查询。
对于 HQL 没有什么多讲的,如果熟悉 sql 的人应该也是能够很快就学会 HQL ,而如果不熟悉 sql 的人那也没关系, HQL 的上手是非常容易的,具体请参考《 Hibernate Reference 》。
3.3.2.占位符式的查询
占位符式的查询 ( 就是采用 ? 替换查询语句中的变量 ) 是在采用 sql 的情况下经常使用的一种查询方式,也是查询时推荐使用的一种方式。
Hibernate 中的查询参数主要有两种类型:值类型和实体类型,值类型就是指一个切实的值 ( 如 String 、 int 、 List 这些 ) ,实体类型就是一个具体的实体,如编写的 User 、 Organization 等,值类型的查询和普通 sql 几乎一样,而实体类型的查询就体现了 Hibernate 的强项, ^_^ ,可以起到简化 sql 的作用,并且使得查询语句更加容易理解。
3.3.2.1.值类型
3.3.2.1.1.简单值
举例如下:
from User u where u.name=:username and u.yearold=:yearold
这就是一个常见的简单值的占位符式的查询,通过这样的方式就可以把值注入到参数中:
query.setParameter(“username”,”bluedavy”);
query.setParameter(“yearold”,25);
同样, hibernate 也支持和 sql 完全相同的 ? 的方式,那么上面的语句以及注入参数的方式就变为了:
from User u where u.name=? and u.yearold=?
query.setParameter(0,”bluedavy”);
query.setParameter(1,25);
推荐使用第一种,那样参数的意义更容易被理解。
3.3.2.1.2. in 查询
in 查询也是经常被使用到的一种查询,在 Hibernate 中表现出来会稍有不同,不过如果按照对象观点去看就很容易理解了,例如下面这句:
from User u where u.name in (:usernameList)
在 Hibernate 中通过这样的方式将值注入到这个参数中:
List list=new ArrayList();
list.add(“jerry”);
list.add(“bluedavy”);
query.setParameterList(“usernameList”,list);
在 sql 中通常是组装一个由 , 连接的值来构成 in 中的参数值,而在 Hibernate 中则依照对象转化为采用 list 了, ^_^ ,是不是更方便些。
3.3.2.2.实体类型
在Hibernate中关联采用的都是对象形式,表现对外就是隐藏了数据库的外键的部分,这也就对习惯使用sql查询的人带来一个问题,因为无法再操作外键字段,那么在涉及到关联的实体的查询时应该怎么做呢,我把它分为单实体和实体集合两种情况来说说。
3.3.2.2.1.单实体
单实体的查询对应到 sql 情况通常是在一对多的情况下通过多端查询同时结合一端的一些过滤条件,在 sql 中通常采用 join 的方式来实现这个,而在 Hibernate 中要实现这点就更容易了,举例如下:
User 和 Organization 是一对多,现在要查询属于组织机构名称为 ”Blogjava” 以及用户年龄大于 20 的用户:
from User u where u.org.name=:orgname and u.yearold>:yearold
query.setParameter(“orgname”,”Blogjava”);
query.setParameter(“yearold”,20);
可以看到这样的查询语句比 sql 更简单多了,同时也更容易理解多了。
3.3.2.2.2.实体集合
实体集合过滤形式的查询在实际的项目中也经常会碰到,仍然用上面的例子,但改为通过 Organization 去查询:
from Organization org where org.name=:orgname and org.users.yearold>:yearold
是不是比 sql 简单多了,而且更容易理解呢, ^_^
这个时候对象化查询语句的优势就体现出来了,而不用陷入 sql 的那种关系型的通过外键进行查询的方式。
3.3.3.NamedQuery
NamedQuery 的意思就是指在 PO 的映射文件中定义关于 PO 的查询语句,而在应用中指需要直接调用此查询语句的别名即可,这个好处非常明显,使得所有的查询语句可以统一的进行管理,同样,我们可以在 PO 中通过 javadoc 的方式进行定义,这就更方便了, ^_^
操作 NamedQuery 的方法和普通 hql 的方法基本一样:
session.getNamedQuery(queryname);
其中的 queryname 就是我们定义的查询语句的别名,一个 namedQuery 的语句的示例如下:
< query name = "validate" ><![CDATA[
from User u where u.loginname=:loginname and u.password=:password
]]></ query >
3.3.4. Criteria
条件查询的 API 使得我们可以采用完全对象化的方式进行实体的查询,而不是通过 hql 的方式,在实际项目中,使用 hql 的方式更为居多,毕竟写起来更方便。
关于 Criteria 的具体介绍请参阅《 Hibernate Reference 》。
3.3.5.原生 SQL
原生 SQL 不推荐使用,但在某些确实需要用 sql 的情况下那么 Hibernate 还是支持的,具体见《 Hibernate Reference 》。
四.设计
独立的编写这个章节的原因是希望在采用Hibernate的情况下充分的去发挥Hibernate的优势,改变我们以关系形式去做持久层的设计的惯性思维,形成以OO的思想去设计持久层,所以我非常推荐通过写PO去生成数据表的方式,而不是设计表反向导成PO的形式,当然,对于原有的系统那就没办法了。
OO 思想中的核心三要素:封装、继承和多态,在Hibernate的支持下同样可以充分发挥OO的三要素来优化持久层的设计。
4.1. 封装
4.1.1. Component
Hibernate 中有一个 Component 的概念,这就允许在进行持久层设计的时候采用细粒度级的领域模型进行设计,例如在 User 对象中需要记录 User 的 firstname 、 lastname 这些信息,而在其他的表中也有这种需求,那么在 Hibernate 中我们就可以把 firstname 、 lastname 组装为一个 UserName 对象,作为 Component 放入 User 中,在 user 中就可以变为采用 user.getUserName.getFristName 的方式来获取。
Component 对于我们采用对象的封装概念进行持久层设计提供了很好的支持,同时在 Hibernate 中还有 Elements 、 Properties 这些元素,具体请参见《 Hibernate Reference 》。
4.2. 继承
继承使得我们可以对持久层中的对象进行抽象,类如我们可以形成Person这个对象,而User、Employee都继承自这个对象。
继承在数据库形式的设计中固然也可以实现,但通常不能以对象的观点去发挥的淋漓尽致,当然不是说以对象的方式去设计一定是最好的。
在Hibernate中对于继承映射到数据表有几种不同的策略,各有适用的不同场合,具体的解释和说明见《Hibernate Reference》
4.2.1.单表策略
单表策略很容易理解,就是将类、子类中所有的属性都放至一张表中,这对于子类属性不多的情况非常有效。
在 Hibernate 中通常将子类定义为 @hibernate.subclass 的方式来实现这个策略。
4.2.2.每个子类一张表
每个子类一张表在 Hibernate 中有几种实现方式, @hibernate.join-subclass 、 @hibernate.join-subclass-key 的组合方式以及 @hibernate.join-subclass 、 @hibernate.discriminator 的组合方式是较常用的两种方式,第一种方式采用的是主键关联方式,第二种方式采用的是 discriminator 字段的关联方式,个人比较推崇第一种方式。
这种策略适合在子类属性和父类有较大不同的情况下采用。
4.2.3.每个具体类一张表
这种策略适合在类层次结构上有一定数量的抽象类的情况下使用,同样有两种方式,一种是采用显式多态的方式,另一种是采用隐式多态的方式,显式多态采用的为 @hibernate.union-subclass 的方式,隐式多态则采用每个具体类的 PO 独立建表的策略,在它的映射文件中将看不出任何的和接口、抽象类的关系,同时对于抽象类,需要指明其 abstract=”true” 。
4.3. 多态
4.3.1.查询
在查询中很容易体现 Hibernate 对于多态的支持,如系统有 Person 对象、 User 和 Employee 分别继承自 Person ,同时 Person 和 Organization 对象关联,这个时候我们通过 Organization 获取其关联的 Person 时得到的既有可能是 User ,也有可能是 Employee , ^_^…
五.性能
Hibernate 作为ORM工具,从性能上来讲带给了很多人忧虑,但我觉得Hibernate在性能上也许会带来少许的降低,但如果对于不能合理设计数据库和使用SQL的人来说,我觉得Hibernate反倒能提高性能,除非是在一些特殊的场合,如报表式的那种查询推荐继续采用JDBC的方式。
Hibernate 在性能提升上其实有很多种做法,在《Hibernate Reference》中也有专门的提升性能的章节,在这里我提几点在项目中通常采用的方法。
5.1. Lazy Load
Lazy Load 是常用的一种提升性能的方法,这个其实很容易理解,在不采用lazy load的情况下,Hibernate在获取一个PO的时候,将同时获取PO中的属性、PO中的集合以及集合中对象的属性、集合,这样看过去很容易看出,如果对象的关联结构有深层次的话,最后搞不好整个库都被加载出来了,而在实际使用中往往可能只需要用到PO中的一两个属性而已,这点也是之前的ORM产品经常被批的一点,就是ORM产品不能象sql那样只获取需要的东西,^_^,其实Hibernate在这点上一直就支持,而且支持的还不错,在Hibernate 3以后,默认的lazy就已经设置为true了,这个时候包括po中的属性都是采用lazy load的方式,只有在调用到这个属性时才会从缓存或数据库中加载,当然,集合也同样如此。
在lazy load上推荐不要什么字段都采用lazy load的方式,对于一些基本属性的字段建议将其lazy设置为false,而对于一些可能需要消耗内存的字段,如clob这样的字段对象的lazy设置为true,对于集合则全部设置为lazy=true。
是否采用Lazy load对系统的性能会有非常明显的影响,同时尽量不要将Detached Object放入Http的session中。
5.1.1.OSIV
OSIV : Open Session In View ,在 B/S 系统中通常采用这种方式来更好的去支持 Lazy load ,意思就是在 View 加载前打开 Session ,在 View 加载完毕后关闭 Session 的方式,在 Spring 中有 OpenSessionInViewFilter ,可参考或直接使用。
5.2. Cache
Cache 是在提升系统性能方面常用的方法,在Hibernate中通常有非常好的对于Cache的支持方法,Hibernate中对于Cache有一级缓存和二级缓存的概念,一级缓存是必须的,位于Session部分,二级缓存则不是必须的,由开发人员自行指定,二级缓存可指定使用何种开源的cache工具,Hibernate 3以后的版本默认使用的是Ehcache,也可以切换为Oscache、JbossCache,对我而言最重要的区别在于对于cluster的支持上。
二级缓存能够明显的提高系统的性能,当然,同时也会更加的消耗内存,可以通过配置文件来指定内存中能够加载的最多的元素,这有利于避免消耗过多内存。
二级缓存的设置在Hibernate中非常的简单,只需要在相应的hbm.xml中增加cache元素,指明使用何种策略,如read-only、read-write等,也可以直接在hibernate.cfg.xml中增加class-cache的方式来进行全局指定。
5.3. 高效的查询语句
查询语句的是否高效对于系统的性能也是会造成明显的影响的,为了方便系统性能的调优,建议大家对查询语句进行统一管理,如统一采用NamedQuery的方式,在这样的情况下可以在系统运行时请教数据库专家,由他们来分析系统中的查询语句的执行效率以及提出改进策略,而对于开发人员来讲,在查询语句方面最能够注意的就是采用占位符式的查询。
5.3.1.占位符式的查询
数据库对于所有的 sql 语句都要进行语法分析,而其分析通常会受到语句中的大小写、空格以及参数不同的影响,在其语法分析器认为不同的情况下将再次进行分析,这就不可避免的降低了响应的速度,而采用占位符式的查询则可保证语法分析器只进行一次的分析,在参数不同的情况并不会出现重复解析的现象,其次就是要统一查询语句的编写风格,包括大小写、空格这些。
我不是很确定 Hibernate 中对于语句的语法分析,估计和数据库的这种方式应该差不多,不过猜想可能会更智能一些, ^_^
5.4. 一些配置
在 hibernate.cfg.xml 中的一些配置也会对性能产生一定的影响,如 jdbc.fetch_size 的设置等,还有象采用连接池方面的设置,对于 B/S 应用的情况建议尽量采用应用服务器提供的 JNDI 的方式。
5.5. 建议
在性能提升方面从两方面入手,一是持久层对象的设计上,这方面可以参考《 Hibernate Reference 》中提升性能章节中的一些建议,另一方面则是请教数据库专家,由数据库专家对表结构、查询语句等进行分析来给出改进策略,在现有的一个项目中,竟然有出现 Hibernate 在外键上没建立索引的现象出现?
六. 测试
6.1. 编写专门的测试用的配置文件
测试方面也是极度关心的话题,在测试方面其实比较简单,只需要在测试类中采用专门用于测试的配置文件即可,在这个配置文件中,通过都是采用设置hbm2ddl.auto属性为create-drop的方式,也就是在测试类运行前创建表,在测试类运行后删除表的策略,在更多的情况下,我们可以采用in-memory的数据库的方式,如hsql,当然,有些时候则需要和实际运行环境一致,那么就需要采用建立专门的测试库的方式,避免测试数据和运行数据的相互影响。
七. 企业应用开发
事务和并发是企业应用开发中非常关注的两个话题,在《Hibernate Reference》中提供了详细的方案,在这里我就简单的说说。
7.1. 事务
事务是企业应用开发中非常重视的一点,而在Hibernate中操作此部分和sql方式没有什么很大的区别,可以通过session主动去获取Transaction来实现事务控制,同时也可以交由应用服务器提供的JTA来实现事务控制。
在事务这个级别上如果有更高的要求,建议采用Spring的事务框架。
7.2. 并发
在并发方面多采用锁策略,锁策略和数据库基本相同,同样是乐观锁和悲观锁两种策略,乐观锁策略在Hibernate中推荐使用version或timestamp来实现,具体覆盖方式则需要根据应用而定,如是采用最新的修改的覆盖还是采用版本冲突策略等,悲观锁策略则通过指定对象的锁方式,如LockMode.READ,引用《Hibernate Reference》中的一段话:
“用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定,或者在一个新的事务启动的时候,重新进行锁定。Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!
如果数据库不支持用户设置的锁定模式,Hibernate将使用适当的替代模式,这一点可以确保应用程序的可移植性。”。
用户可通过几种方式来指定锁定模式:
u Session.load() 的时候指定锁定模式LockMode;
u Session.lock() ;
u Query.setLockMode() 。
八. 相关书籍
Hibernate 上手并不难,但要真正的用好它确实不是件容易的事,有些书籍能够很好的帮我们快速的提供解决思路和解决方案,而这些书籍我们也应该常备,以方便自己在有些问题上的解答。
同时,我一直坚持的观点,一种开源框架通常带来的不仅仅是开发、使用上的改变,带来的最大的改变仍然是在设计层次上的,设计上能否充分的发挥开源框架的优势才是最为重要的。
8.1. 《Hibernate Reference》
这本没什么说的,必读书籍,也许在读的时候很多东西你不会觉得什么,但当碰到一些确定方向的问题时,可以通过此书快速的查找到相应的解决方案,感谢Redsaga组织的翻译工作,使得我们可以有中文版可看。
目前版本(Hibernate 3.1.2)的下载地址:
http://www.redsaga.com/hibernate-ref/3.1.2/zh-cn/pdf/hibernate_reference.pdf
8.2. 《Hibernate in action》
In action 系列的书籍也没啥多说的,强烈推荐看看,尽管现在看起来版本有些老了,但里面很多的实践思想仍然是非常值得学习的,网上应该有很多电子版下载的地方。
8.3. 《深入浅出Hibernate》
这本书想必大家也听闻了不少,简称白皮书,^_^,是夏昕、曹晓刚以及唐勇三位大师的大作。
相关推荐
**hibernate实践** 在Java世界中,Hibernate是一个强大的对象关系映射(ORM)框架,它简化了数据库操作,使得开发者可以使用面向对象的方式处理数据。这篇文档集合了丰富的Hibernate实践知识,对于初学者来说是一份...
Hibernate 是一个开源的O/R mappimg的框架,基于JDBC提供了一种持久性数据管理的方案,相对于EntityBean来说是相当轻量级的。由于Hibernate是基于 JDBC的,所以它的数据库查寻的能力相对于CMP来说也是异常强大的,...
### Hibernate实践概述与知识点 #### 一、单表操作概览与关键技术点 **环境准备:** 进行Hibernate实践,首先需要一系列的环境搭建,包括数据库(本例为Oracle9i)、数据库驱动(ojdbc14.jar)、Hibernate核心库...
在这个“hibernate 实践 - Query详解”的主题中,我们将深入理解如何使用Query接口来执行SQL查询,并绑定参数。 1. **Query接口** Query接口是Hibernate提供的一种用于执行HQL(Hibernate查询语言)或SQL查询的API...
### Hibernate实践:数据库操作 #### 一、引言 Hibernate是一个强大的对象关系映射(ORM)框架,它简化了Java应用程序与数据库之间的交互过程。在实际应用Hibernate进行开发的过程中,虽然能够享受到它带来的诸多...
**标题:手写Hibernate实践详解** 在Java Web开发中,Hibernate是一个非常重要的对象关系映射(ORM)框架,它极大地简化了数据库与Java对象之间的交互。"第一个手写Hibernate"项目旨在帮助开发者深入理解Hibernate...
`第二章mysql.sql` 文件可能是用于配合 Hibernate 实践的 MySQL 数据库脚本,包含了创建对应表的 SQL 语句。而 `H02` 文件名可能表示课程的第二个部分,其中可能包含更多的实践示例或扩展概念。 学习 Hibernate 的...
4. **映射文件(hbm.xml)**:虽然在现代Hibernate实践中,通常更倾向于使用注解来定义映射,但传统的XML映射文件仍然有价值。映射文件定义了实体类和数据库表之间的详细关系,包括列映射、关联关系等。 5. **...
在现代的Hibernate实践中,更倾向于使用注解(Annotations)来替代XML映射。 然后,我们需要一个SessionFactory,它是Hibernate的主要工作单元,负责创建Session。SessionFactory通过读取配置文件和映射信息被初始...
### Hibernate实践:深入理解与应用 #### 一、Hibernate简介及重要性 Hibernate是一个开源的对象关系映射(ORM)框架,用于Java环境下的数据库交互。它简化了数据持久化层的开发工作,允许开发者以面向对象的方式...
开始Hibernate实践的第一步是搭建项目环境,这包括添加必要的库依赖、配置Hibernate的hibern.cfg.xml文件以及实体类的映射文件(如.hbm.xml)。同时,需要创建数据库并建立相应的表结构。 3. **实体类与数据表映射...
不过,在现代的Hibernate实践中,更常见的是使用注解方式来替代XML映射,使得映射信息直接嵌入到实体类中,便于维护和理解。 生成这些文件的过程如下: 1. 安装并配置Hibernate Tools或类似工具。 2. 连接数据库,...
《Hibernate从实践到精通》是一本深度探讨Java持久化框架Hibernate的专业教程,旨在帮助开发者从基础知识到高级技术全面掌握Hibernate的使用。通过实例演示,它深入浅出地讲解了Hibernate的核心概念和技术,使得读者...
总的来说,这个“hibernate小试”资源提供了一个基础的Hibernate实践教程,涵盖了数据库连接配置、实体类定义、映射文件创建、以及基本的CRUD操作。对于初学者来说,这是一个很好的起点,帮助理解Hibernate的工作...
《深入解析osworkflow2.8整合Spring与Hibernate实践》 osWorkflow是一款开源的工作流引擎,它主要用于处理业务流程中的各种任务调度和流转。在本文中,我们将深入探讨如何将osWorkflow2.8版本与Spring和Hibernate...
本压缩包中的学习资料包括了“Hibernate reference”和“Hibernate实践”等内容,旨在帮助你深入理解和熟练运用Hibernate。 一、Hibernate核心概念 1. 对象关系映射(ORM):Hibernate是ORM框架的代表,它通过映射...
**在线论坛BBS的Hibernate实践** 1. **数据库设计**:首先,我们需要设计论坛的数据库结构,包括用户表(User)、帖子表(Post)、评论表(Comment)等。每个表的字段应该对应实体类的属性。 2. **实体类创建**:...
【Hibernate实践探索】 在深入探讨Hibernate这一强大的Java对象关系映射(ORM)框架之前,我们需要先理解ORM的基本概念。ORM是将数据库中的表映射为Java对象,使得开发者可以使用面向对象的方式来操作数据库,简化...