BlueDavy之技术Blog

理论不懂就实践,实践不会就学理论!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  674 随笔 :: 2 文章 :: 1263 评论 :: 1 Trackbacks

一.             

在实际项目中使用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开发的效率,通常都需要使用一些辅助工具,如xdocletmiddlegen等。

尽管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 ToolsEclipse 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的属性来决定是采用threadjta或自定义的方式来产生session

2.5. CommonDao

在持久层部分目前采用的较多的仍然是dao模式,Hibernate作为ORM工具已经提供了CRUD的封装,类如可以使用session.save()session.persist()这样简单的方式来完成CRUD的操作,但在实际的项目中还是需要提供一个通用的Dao,来简化对于事务、异常处理以及session的操作,同时提供一些项目中需要的相关操作。

三.              开发

在完成了Hibernate的开发环境的搭建后,就可以基于Hibernate进行持久层的开发了,对于持久层开发来说,会涉及到实体的编写、实体的维护以及实体的查询三个部分。

3.1. 实体的编写

Hibernate 的一个明显的优点就是在于可透明化的对对象进行持久,这也就意味着持久对象根本就不需要依赖任何的东西,可以采用POJO的方式来编写,在Hibernate 3以上版本还提供了对于MapXML的方式的持久的支持,就更方便了,在项目中,更多采用的仍然是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中可采用多种方式来配置一对多的关联,如采用SetListBagMap等,具体在《Hibernate Reference》中都有详细说明。

在这里我想说的一点就是关于inverse的设置,在一对多的情况下建议将一端的inverse设为true,而由多端去自动维护关联关系,为什么这样做其实挺容易理解的,假设orguser为一对多的关联,org.getUsers