|
Hibernate 开发指南 Original Author: 夏昕<[url=mailto:xiaxin@gmail.com]xiaxin@gmail.com> 本文是由笔者2003 年底一个咨询项目中,为客户做的持久层设计培训 文案整理而来。 其中的内容涉及Hibernate 的使用,以及一部分笔者实际咨询项目中的 经验积累,另一方面,大部分是笔者在Hibernate 的官方论坛中与众多 技术专家交流所得。 既来于斯,则归于斯。希望能聊有所用。 本文并非试图替代Hibernate Reference ,相对而言,Hibernate Reference 的编写目的是为开发者提供更简便的条目索引,而本文目标则在于为开 发人员提供一个入门和掌握Hibernate 的途径。 本文需结合Hibernate Reference 使用。 笔者好友曹晓钢义务组织了Hibernate 文档的汉化工作,在此对其辛勤劳作致敬。 中文版Hibernate Reference 将被包含在Hibernate 下个官方Release 中,目前可 通过http://www.redsaga.com 获取中文版Hibernate Reference 的最新版本。 本文中如果发现问题和错误,请随时联系笔者,以免误导他人。 本文转载不限,不过请保持本文完整。万分感谢!
Hibernate 开发指南.......................................................................................................1 准备工作..........................................................................................................3 构建Hibernate 基础代码...............................................................................3 由数据库产生基础代码...........................................................................4 Hibernate 配置..............................................................................................15 第一段代码....................................................................................................17 Hibernate 基础语义......................................................................................19 Configuration ........................................................................................19 SessionFactory.......................................................................................20 Session ....................................................................................................20 Hibernate 高级特性......................................................................................................22 XDoclet 与Hibernate 映射...........................................................................22 数据检索........................................................................................................31 Criteria Query...............................................................................31 Criteria 查询表达式................................................................31 Criteria 高级特性....................................................................33 限定返回的记录范围.............................................................33 对查询结果进行排序.............................................................33 Hibernate Query Language (HQL).........................................34 数据关联........................................................................................................35 一对一关联.............................................................................35 一对多关联.............................................................................37
. 单向一对多关系......................................................37 . 双向一对多关系......................................................42 多对多关联.............................................................................47 数据访问........................................................................................................54 PO 和VO ...............................................................................................54 关于unsaved-value ...............................................................................57 Inverse 和Cascade.........................................................................59 延迟加载(Lazy Loading)............................................................59 事务管理........................................................................................................63 基于JDBC的事务管理:.....................................................................64 基于JTA的事务管理:.......................................................................65 锁(locking).........................................................................................68 悲观锁(Pessimistic Locking) .......................................68 乐观锁(Optimistic Locking)..........................................69 Hibernate 分页..........................................................................................73 Cache管理....................................................................................................75 Session 管理...............................................................................................79 编后赘言................................................................................................................84
Hibernate Quick Start 准备工作 1. 下载Ant 软件包,解压缩(如C:\ant\)。并将其bin 目录(如c:\ant\bin )添加到系统 PATH 中。 2. 下载Hibernate 、Hibernate-Extension 和Middlegen-Hibernate 软件包的最新版本。 http://prdownloads.sourceforge.net/hibernate/ 构建Hibernate 基础代码 Hibernate 基础代码包括: 1. POJO POJO 在Hibernate 语义中理解为数据库表所对应的Domain Object 。这里的POJO 就是所谓的“Plain Ordinary Java Object”,字面上来讲就是无格式普通Java 对象,简 单的可以理解为一个不包含逻辑代码的值对象(Value Object 简称VO)。 一个典型的POJO: public class TUser implements Serializable {
private String name;
public User(String name) { this.name = name; }
/** default constructor */
public User() {
}
public String getName() { return this.name; }
public void setName(String name) { this.name = name; } }
2. Hibernate 映射文件 Hibernate 从本质上来讲是一种“对象-关系型数据映射”(Object Relational Mapping 简称ORM)。前面的POJO 在这里体现的就是ORM 中Object 层的语义, 而映射(Mapping)文件则是将对象(Object)与关系型数据(Relational)相关联
的纽带,在Hibernate 中,映射文件通常以“.hbm.xml”作为后缀。 构建Hibernate 基础代码通常有以下途径: 1. 手工编写 2. 直接从数据库中导出表结构,并生成对应的ORM 文件和Java 代码。 这是实际开发中最常用的方式,也是这里所推荐的方式。 通过直接从目标数据库中导出数据结构,最小化了手工编码和调整的可能性,从而 最大程度上保证了ORM 文件和Java 代码与实际数据库结构相一致。 3. 根据现有的Java 代码生成对应的映射文件,将Java 代码与数据库表相绑定。 通过预先编写好的POJO 生成映射文件,这种方式在实际开发中也经常使用,特别 是结合了xdoclet 之后显得尤为灵活,其潜在问题就是与实际数据库结构之间可能 出现的同步上的障碍,由于需要手工调整代码,往往调整的过程中由于手工操作的 疏漏,导致最后生成的配置文件错误,这点需要在开发中特别注意。 结合xdoclet ,由POJO 生成映射文件的技术我们将在“高级特性”章节中进行探讨。 由数据库产生基础代码 通过Hibernate 官方提供的MiddleGen for Hibernate 和Hibernate_Extension 工具包,我 们可以很方便的根据现有数据库,导出数据库表结构,生成ORM 和POJO 。 1) 首先,将Middlegen-Hibernate 软件包解压缩(如解压缩到C:\Middlegen\ )。 2) 配置目标数据库参数 进入MiddleGen 目录下的\config\database 子目录,根据我们实际采用的数据库打开 对应的配置文件。如这里我们用的是mysql 数据库,对应的就是mysql.xml 文件。 <property name="database.script.file" value="${src.dir}/sql/${name}-mysql.sql"/>
<property name="database.driver.file" value="${lib.dir}/mysql.jar"/>
<property name="database.driver.classpath" value="${database.driver.file}"/>
<property name="database.driver" value="org.gjt.mm.mysql.Driver"/>
<property name="database.url" value="jdbc:mysql://localhost/sample"/>
<property name="database.userid" value="user"/>
<property name="database.password" value="mypass"/>
<property name="database.schema" value=""/>
<property name="database.catalog" value=""/>
<property name="jboss.datasource.mapping" value="mySQL"/>
其中下划线标准的部分是我们进行配置的内容,分别是数据url 以及数据库用 户名和密码。 3) 修改Build.xml 修改MiddleGen 根目录下的build.xml 文件,此文件是Middlegen-Hibernate 的Ant 构建配置。Middlegen-Hibernate 将根据build.xml 文件中的具体参数生成数据库表映射 文件。可配置的项目包括: a) 目标数据库配置文件地址 查找关键字”!ENTITY”,得到:
<!DOCTYPE project [ <!ENTITY database SYSTEM "file:./config/database/hsqldb.xml"> ]>
默认情况下,MiddleGen 采用的是hsqldb.xml,将其修改为我们所用的数据 库配置文件(mysql.xml): <!DOCTYPE project [ <!ENTITY database SYSTEM "file:./config/database/mysql.xml"> ]>
b) Application name 查找: <property name="name" value="airline"/>
“aireline”是MiddleGen 原始配置中默认的Application Name,将其修改为我们 所希望的名称,如“HibernateSample”: <property name="name" value="HibernateSample"/>
c) 输出目录 查找关键字“name="build.gen-src.dir"”,得到: <property name="build.gen-src.dir" value="${build.dir}/gen-src"/>
修改value="${build.dir}/gen-src"使其指向我们所期望的输出目录, 这里我们修改为: <property name="build.gen-src.dir" value="C:\sample"/>
d)对应代码的Package name 查找关键字“destination”,得到: <hibernate
destination="${build.gen-src.dir}"
package="${name}.hibernate" genXDocletTags="false" genIntergratedCompositeKeys="false" javaTypeMapper=
"middlegen.plugins.hibernate.HibernateJavaTypeMapper"
/>
可以看到, hibernate 节点package 属性的默认设置实际上是由前面的 Application Name (${name})和“.hibernate”组合而成,根据我们的需要, 将其改为: <hibernate
destination="${build.gen-src.dir}"
package="org.hibernate.sample"
genXDocletTags="true"
genIntergratedCompositeKeys="false"
javaTypeMapper=
"middlegen.plugins.hibernate.HibernateJavaTypeMapper"
/>
这里还有一个属性genXDocletTags,如果设置为true ,则生成的代码将包含
xdoclet tag,这为以后在开发过程中借助xdoclet 进行映射调整提供了帮助。关 于Hibernate 的xdoclet 使用,请参见“高级特性”中的相关内容。 注意,如果使用的数据库为SQLServer,需要将build.xml 中如下部分(下划 线部分)删除,否则Middlegen 会报出找不到表的错误。 <middlegen
appname="${name}" prefsdir="${src.dir}" gui="${gui}" databaseurl="${database.url}" initialContextFactory="${java.naming.factory.initial}" providerURL="${java.naming.provider.url}" datasourceJNDIName="${datasource.jndi.name}" driver="${database.driver}" username="${database.userid}" password="${database.password}" schema="${database.schema}" catalog="${database.catalog}"
>
至此为止,MiddleGen 已经配置完毕,在MiddleGen 根目录下运行ant,就将出现 MiddleGen 的界面:
① ②③ ④ ⑤ ⑥⑦ ⑨ ⑧ ⑩ 可以看到,数据库中的表结构已经导入到MiddleGen 的操作界面中,选定数据库 表视图中的表元素,我们即可调整各个数据库表的属性。 1 Domain Class Name 对应POJO 的类名 2 Key Generator 主键产生器 可选项说明:
1) Assigned
主键由外部程序负责生成,无需Hibernate 参与。 2) hilo 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主 键生成历史状态。 3) seqhilo 与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史 状态保存在Sequence 中,适用于支持Sequence 的数据库,如Oracle 。 4) increment 主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持 一个变量,以保存着当前的最大值,之后每次需要生成主键的时候 将此值加1 作为主键。 这种方式可能产生的问题是:如果当前有多个实例访问同一个数据 库,那么由于各个实例各自维护主键状态,不同实例可能生成同样 的主键,从而造成主键重复异常。因此,如果同一数据库有多个实 例访问,此方式必须避免使用。 5) identity 采用数据库提供的主键生成机制。如DB2 、SQL Server 、MySQL 中的主键生成机制。 6) sequence 采用数据库提供的sequence 机制生成主键。如Oralce 中的 Sequence 。 7) native 由Hibernate 根据底层数据库自行判断采用identity 、hilo 、sequence 其中一种作为主键生成方式。 8) uuid.hex 由Hibernate 基于128 位唯一值产生算法生成16 进制数值(编码后 以长度32 的字符串表示)作为主键。 9) uuid.string 与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些 数据库中可能出现问题(如PostgreSQL)。 10) foreign 使用外部表的字段作为主键。 一般而言,利用uuid.hex 方式生成主键将提供最好的性能和数据库平台适 应性。
另外由于常用的数据库,如Oracle 、DB2 、SQLServer 、MySql 等,都提 供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数 据库提供的主键生成机制上,采用generator-class=native 的主键生成方式。 不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳, 大量并发insert 数据时可能会引起表之间的互锁。 数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状 态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量), 之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之 后再把这个新的最大值更新回内部表中,这样,一次Insert 操作可能导致数据 库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生 了较大影响。 因此,对于并发Insert 要求较高的系统,推荐采用uuid.hex 作为主键生成 机制。 3 如果需要采用定制的主键产生算法,则在此处配置主键生成器,主键生成器必 须实现net.sf.hibernate.id.IdentifierGenerator 接口。 4 Schema Name 数据库Schema Name 。 5 Persister 自定义持久类实现类类名。如果系统中还需要Hibernate 之外的持久层实 现机制,如通过存储过程得到目标数据集,甚至从LDAP 中获取数据来填 充我们的POJO 。 6 Enable proxies 是否使用代理(用于延迟加载[Lazy Loading])。 7 Dynamic Update 如果选定,则生成Update SQL 时不包含未发生变动的字段属性,这样可 以在一定程度上提升SQL 执行效能。 8 Mutable 类是否可变,默认为选定状态(可变)。如果不希望应用程序对此类对应 的数据记录进行修改(如对于数据库视图),则可将取消其选定状态,之 后对此类的Delete 和Update 操作都将失效。 9 Implement the Lifecyle interface 是否实现Lifecyle 接口。Lifecyle 接口提供了数据固化过程中的控制机制,
通过实现Lifecyle 接口,我们可以在数据库操作中加入回调(Call Back) 机制,如在数据库操作之前,之后触发指定操作。 10 Implement the Validatable interface 是否实现Validatable 接口。通过实现Validatable 接口,我们可以在数据被 固化到数据库表之前对其合法性进行验证。 值得注意的是,通过实现Lifecyle 接口,我们同样可以在数据操作之前验 证数据合法性,不同的是,Validatable 接口中定义的validate 方法可能会 被调用多次,因此设计中应避免在Validatable 接口的validate 方法实现中 加入业务逻辑的验证。 以上是针对Class 的设置,同样,在MiddleGen 中,我们也可以设定字段属性。在 MiddleGen 中选定某个字段,界面下方即出现字段设置栏:
① ② ③ ④ ⑤ 在这里我们可以设置字段的属性,其中: 1 Hibernate mapping specialty 映射类型: Key :主键 Property :属性 Version :用于实现optimistic locking,参见“高级特性”章节中关 于optimistic locking 的描述 2 Java property name
字段对应的Java 属性名 3 Java Type 字段对应的Java 数据类型
4 Column updateable 生成Update SQL 时是否包含本字段。
5 Column insertable 生成Insert SQL 时是否包含本字段。
单击窗口顶部的Generate 按钮,MiddleGen 即为我们生成这些数据库表所对应的 Hibernate 映射文件。在MiddleGen 根目录下的\build\gen-src\net\hibernate\sample 目录中, 我们可以看到对应的以.hbm.xml 作为后缀的多个映射文件,每个映射文件都对应了数 据库的一个表。 仅有映射文件还不够,我们还需要根据这些文件生成对应的POJO 。 POJO 的生成工作可以通过Hibernate Extension 来完成,Hibernate Extension 的 tools\bin 目录下包含三个工具: 1. hbm2java.bat 根据映射文件生成对应的POJO 。通过MiddleGen 我们已经得到了映射文件, 下一步就是通过hbm2java.bat 工具生成对应的POJO 。 2. class2hbm.bat 根据POJO class 生成映射文件,这个工具很少用到,这里也就不再详细介绍。 3. ddl2hbm.bat 由数据库导出库表结构,并生成映射文件以及POJO 。这个功能与MiddleGen 的功能重叠,但由于目前还不够成熟(实际上已经被废弃,不再维护),提供 的功能也有限,所以我们还是采用MiddleGen 生成映射文件,之后由hbm2java 根据映射文件生成POJO 的方式。 为了使用以上工具,首先我们需要配置一些参数,打开tools\bin\setenv.bat 文件,修改 其中的JDBC_DRIVER 和HIBERNATE_HOME 环境变量,使其指向我们的实际JDBC Driver 文件和Hibernate 所在目录,如 set JDBC_DRIVER=c:\mysql\mysql.jar set HIBERNATE_HOME=c:\hibernate 同时检查一下环境变量CP 中的各个项目中是否实际存在,特别是%CORELIB% 下的jar 文件,某些版本的发行包中,默认配置中的文件名与实际的文件名有所出入(如 %CORELIB%\commons-logging.jar, 在Hibernate 发行包中,可能实际的文件名是
commons-logging-1.0.3.jar,诸如此类)。 使用hbm2java,根据MiddleGen 生成的映射文件生成Java 代码: 打开Command Window ,在tools\bin 目录下执行: hbm2java c:\sample\org\hibernate\sample\*.xml --output=c:\sample\ 即可生成对应的POJO 。生成的POJO 保存在我们指定的输出目录下(c:\sample)。
目前为止,我们已经完成了通过MiddleGen 产生Hibernate 基础代码的工作。配置 MiddleGen 也许并不是一件轻松的事情,对于Eclipse 的用户而言,目前已经出现了好几个 Hibernate 的Plugin,通过这些Plugin 我们可以更加轻松的完成上述工作,具体的使用方式 请参见附录。
Hibernate 配置 前面已经得到了映射文件和POJO ,为了使Hibernate 能真正运作起来,我们还需要一 个配置文件。 Hibernate 同时支持xml 格式的配置文件,以及传统的properties 文件配置方式,不过这 里建议采用xml 型配置文件。xml 配置文件提供了更易读的结构和更强的配置能力,可以直 接对映射文件加以配置,而在properties 文件中则无法配置,必须通过代码中的Hard Coding 加载相应的映射文件。下面如果不作特别说明,都指的是基于xml 格式文件的配置方式。 配置文件名默认为“hibernate.cfg.xml”(或者hibernate.properties),Hibernate 初始化期 间会自动在CLASSPATH 中寻找这个文件,并读取其中的配置信息,为后期数据库操作做好 准备。 配置文件应部署在CLASSPATH 中,对于Web 应用而言,配置文件应放置在在 \WEB-INF\classes 目录下。 一个典型的hibernate.cfg.xml配置文件如下: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0. dtd">
<hibernate-configuration>
<!—-SessionFactory 配置--> <session-factory>
<!—-数据库URL --> <property name="hibernate.connection.url">
jdbc:mysql://localhost/sample
</property>
<!—-数据库JDBC驱动--> <property name="hibernate.connection.driver_class">
org.gjt.mm.mysql.Driver
</property>
<!—-数据库用户名--> <property name="hibernate.connection.username">
User
</property>
<!—-数据库用户密码--> <property name="hibernate.connection.password">
Mypass
</property>
<!--dialect ,每个数据库都有其对应的Dialet以匹配其平台特性--> <property name="dialect">
net.sf.hibernate.dialect.MySQLDialect
</property>
<!—-是否将运行期生成的SQL输出到日志以供调试--> <property name="hibernate.show_sql">
True
</property>
<!—-是否使用数据库外连接--> <property name="hibernate.use_outer_join">
True
</property>
<!—-事务管理类型,这里我们使用JDBC Transaction --> <property name="hibernate.transaction.factory_class">
net.sf.hibernate.transaction.JDBCTransactionFactory
</property>
<!—映射文件配置,注意配置文件名必须包含其相对于根的全路径--> <mapping resource="net/xiaxin/xdoclet/TUser.hbm.xml"/> <mapping resource="net/xiaxin/xdoclet/TGroup.hbm.xml"/>
</session-factory> </hibernate-configuration>
一个典型的hibernate.properties 配置文件如下: hibernate.dialect net.sf.hibernate.dialect.MySQLDialect hibernate.connection.driver_class org.gjt.mm.mysql.Driver hibernate.connection.driver_class com.mysql.jdbc.Driver hibernate.connection.url jdbc:mysql:///sample hibernate.connection.username user hibernate.connection.password mypass
第一段代码 上面我们已经完成了Hiberante 的基础代码,现在先从一段最简单的代码入手,感受一 下Hibernate 所提供的强大功能。 下面这段代码是一个JUnit TestCase,演示了TUser 对象的保存和读取。考虑到读者可 能没有JUnit 的使用经验,代码中加入了一些JUnit 相关注释。 public class HibernateTest extends TestCase {
Session session = null;
/**
* JUnit中setUp方法在TestCase初始化的时候会自动调用 * 一般用于初始化公用资源 * 此例中,用于初始化Hibernate Session */ protected void setUp(){ try {
/**
* 采用hibernate.properties配置文件的初始化代码: * Configuration config = new Configuration(); * config.addClass(TUser.class); */ //采用hibernate.cfg.xml配置文件 //请注意初始化Configuration时的差异: // 1.Configuration的初始化方式 // 2.xml 文件中已经定义了Mapping文件,因此无需再Hard Coding 导入 // POJO文件的定义 Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory(); session = sessionFactory.openSession();
} catch (HibernateException e) { e.printStackTrace(); } }
/**
* 与setUp方法相对应,JUnitTestCase 执行完毕时,会自动调用tearDown方法 * 一般用于资源释放 * 此例中,用于关闭在setUp方法中打开的Hibernate Session */ protected void tearDown(){ try { session.close(); } catch (HibernateException e) { e.printStackTrace(); } }
/**
* 对象持久化(Insert)测试方法 * * JUnit 中,以”test”作为前缀的方法为测试方法,将被JUnit自动添加 * 到测试计划中运行 */ public void testInsert(){
try { TUser user = new TUser(); user.setName("Emma");
session.save(user); session.flush();
} catch (HibernateException e) { e.printStackTrace(); Assert.fail(e.getMessage());
} }
/**
* 对象读取(Select)测试 * 请保证运行之前数据库中已经存在name=’Erica’的记录 */ public void testSelect(){ String hql= " from TUser where name='Erica'";
try { List userList = session.find(hql); TUser user =(TUser)userList.get(0); Assert.assertEquals(user.getName(),"Erica");
} catch (HibernateException e) { e.printStackTrace(); Assert.fail(e.getMessage());
} } }
主流IDE,如Eclipse 、Intellij IDEA 和JBuilder 中都内置了JUnit 支持。下面是Eclipse 中运行该代码的结果(在Run 菜单中选择Run as -> JUnit Test 即可):
现在我们已经成功实现了一个简单的TUser 实例的保存和读取。可以看到,程序中通过 少量代码实现了Java 对象和数据库数据的同步,同时借助Hibernate 的有力支持,轻松实现 了对象到关系型数据库的映射。 相对传统的JDBC 数据访问模式,这样的实现无疑更符合面向对象的思想,同时也大大 提高了开发效率。 上面的代码中引入了几个Hibernate 基础语义: 1. Configuration 2. SessionFactory 3. Session 下面我们就这几个关键概念进行探讨。 Hibernate 基础语义 Configuration
正如其名,Configuration 类负责管理Hibernate 的配置信息。Hibernate 运行时需要 获取一些底层实现的基本信息,其中几个关键属性包括:
1. 数据库URL 2. 数据库用户 3. 数据库用户密码 4. 数据库JDBC 驱动类 5. 数据库dialect ,用于对特定数据库提供支持,其中包含了针对特定数据库特性 的实现,如Hibernate 数据类型到特定数据库数据类型的映射等。 使用Hibernate 必须首先提供这些基础信息以完成初始化工作,为后继操作做好准 备。这些属性在hibernate 配置文件(hibernate.cfg.xml 或hibernate.properties)中加以设 定(参见前面“Hibernate 配置”中的示例配置文件内容)。 当我们调用: Configuration config = new Configuration().configure();
时,Hibernate 会自动在当前的CLASSPATH 中搜寻hibernate.cfg.xml 文件并将其读 取到内存中作为后继操作的基础配置。Configuration 类一般只有在获取SessionFactory 时需要涉及,当获取SessionFactory 之后,由于配置信息已经由Hibernate 维护并绑定 在返回的SessionFactory 之上,因此一般情况下无需再对其进行操作。 我们也可以指定配置文件名,如果不希望使用默认的hibernate.cfg.xml 文件作为配 置文件的话: File file = new File("c:\\sample\\myhibernate.xml"); Configuration config = new Configuration().configure(file);
SessionFactory SessionFactory 负责创建Session 实例。我们可以通过Configuation 实例构建 SessionFactory: Configuration config = new Configuration().configure(); SessionFactory sessionFactory = config.buildSessionFactory();
Configuration 实例config 会根据当前的配置信息,构造SessionFactory 实例并返回。 SessionFactory 一旦构造完毕,即被赋予特定的配置信息。也就是说,之后config 的任 何变更将不会影响到已经创建的SessionFactory 实例(sessionFactory)。如果需要 使用基于改动后的config 实例的SessionFactory ,需要从config 重新构建一个 SessionFactory 实例。 Session Session 是持久层操作的基础,相当于JDBC 中的Connection 。 Session 实例通过SessionFactory 实例构建:
Configuration config = new Configuration().configure(); SessionFactory sessionFactory = config.buildSessionFactory(); Session session = sessionFactory.openSession();
之后我们就可以调用Session 所提供的save 、find 、flush 等方法完成持久层操作: Find:
String hql= " from TUser where name='Erica'"; List userList = session.find(hql);
Save:
TUser user = new TUser(); user.setName("Emma"); session.save(user); session.flush();
最后调用Session.flush 方法强制数据库同步,这里即强制Hibernate 将user 实 例立即同步到数据库中。如果在事务中则不需要flush 方法,在事务提交的时候, hibernate 自动会执行flush 方法,另外当Session 关闭时,也会自动执行flush 方法。
Hibernate 高级特性 XDoclet 与Hibernate 映射 在POJO 中融合XDoclet 的映射文件自动生成机制,提供了除手动编码和由数据库导出 基础代码的第三种选择。 本章将结合XDoclet 对Hibernate 中的数据映射进行介绍。 实际开发中,往往首先使用MiddleGen 和hbm2java 工具生成带有XDoclet tag 的POJO (MiddleGen build.xml 中的genXDocletTags 选项决定了是否在映射文件中生成XDoclet Tag, 详见Hibernate Quick Start 章节中关于MiddleGen 的说明)。之后通过修改POJO 中的XDoclet tag 进行映射关系调整。 XDoclet 已经广泛运用在EJB 开发中,在其最新版本里,包含了一个为Hibernate 提供支 持的子类库Hibernate Doclet ,其中包含了生成Hibernate 映射文件所需的ant 构建支持以及 java doc tag 支持。 XDoclet 实现基本原理是,通过在Java 代码加入特定的JavaDoc tag,从而为其添加特定 的附加语义,之后通过XDoclet 工具对代码中JavaDoc Tag 进行分析,自动生成与代码对应 的配置文件,XDoclet 。 在Hibernate-Doclet 中,通过引入Hibernate 相关的JavaDoc tag,我们就可以由代码生成 对应的Hibernate 映射文件。 下面是一个代码片断,演示了Hibernate-Doclet 的使用方式: /**
* @hibernate.class * table="TUser" */ public class TUser implements Serializable {
……
/**
* @hibernate.property * column="name" * length="50" * not-null="true" * * @return String */ public String getName() { return this.name;
}
……
}
以上是使用Hibernate-Doclet 描述POJO(TUser)及其对应表(TUser)之间映射关系 的一个例子。 其中用到了两个hibernate doclet tag,@hibernate.class 和@hibernate.property。 这两个tag 分别描述了POJO 所对应的数据库表信息,以及其字段对应的库表字段信息。 之后Hibernate Doclet 就会根据这些信息生成映射文件: <hibernate-mapping> <class name="net.xiaxin.xdoclet.TUser" table="TUser"
> <property name="name" type="java.lang.String" column="name" not-null="true" length="50" >
</class>
</hibernate-mapping>
这样我们只需要维护Java 代码,而无需再手动编写具体的映射文件即可完成Hibernate 基础代码。 熟记Hibernate-Doclet 众多的Tag,显然不是件轻松的事情,好在目前的主流IDE 都提 供了Live Template 支持。我们只需进行一些配置工作,就可以实现Hibernate-Doclet Tag 的自动补全功能,从而避免了手工编写过程中可能出现的问题。 附录中提供了主流IDE,包括JBuilder,Intellij IDEA,Eclipse 的Hibernate-Doclet 集成 指南。 下面我们就Hibernate Doclet 中常用的Tag 进行探讨,关于Tag 的详细参考,请参见 XDoclet 的官方指南(http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html)以及 Hibernate Reference(http://www.hibernate.org)。 常用Hibernate-Doclet Tag 介绍: 1. Class 层面: 1) @hibernate.class
描述POJO 与数据库表之间的映射关系,并指定相关的运行参数。 参数描述类型必须 table 类对应的表名 默认值:当前类名 Text N dynamic-update 生成Update SQL 时,仅包含发生变动 的字段 默认值: false Bool N dynamic-insert 生成Insert SQL 时,仅包含非空(null) 字段 默认值:false Bool N Proxy 代理类 默认值:空 Text N discriminator-value 子类辨别标识, 用于多态支持。Text N where 数据甄选条件,如果只需要处理库表中某 些特定数据的时候,可通过此选项设定结 果集限定条件。 如用户表中保存了全国所有用户的数据, 而我们的系统只是面向上海用户,则可指 定where=”location=’Shanghai’" Text N
典型场景: /**
* @hibernate.class * table="TUser" (1) * dynamic-update="true" (2) * dynamic-insert="true" (3) * proxy=”” (4) * discriminator-value=”1” (5) */ public class TUser implements Serializable {
……
}
本例中: 1 table参数指定了当前类(TUser)对应数据库表“TUser”。 2 dynamic-update 参数设定为生成Update SQL 时候,只包括当前发生变化的 字段(提高DB Update性能)。 3 Dynamic-insert 参数设定为生成Insert SQL 时候,只包括当前非空字段。 (提高DB Insert性能) 4 Proxy 参数为空,表明当前类不使用代理(Proxy)。代理类的作用是为Lazy Loading提供支持,请参见下面关于Lazy Loading的有关内容。 5 discriminator-value参数设为”1”。 discriminator-value 参数的目的是对多态提供支持。请参见下面关于 @hibernate.discriminator的说明。 2) @hibernate.discriminator @hibernate.discriminator(识别器) 用于提供多态支持。 参数描述类型必须 column 用于区分各子类的字段名称。 默认值:当前类名 text Y type 对应的Hibernate 类型Bool N length 字段长度Bool N 如: TUser 类对应数据库表TUser ,并且User 类有两个派生类SysAdmin 、 SysOperator 。 在TUser 表中,根据user_type 字段区分用户类型。
为了让Hibernate 根据user_type 能自动识别对应的Class 类型(如user_type==1 则自动映射到SysAdmin 类,user_type==2 则自动映射到SysOperator 类),我们需要 在映射文件中进行配置,而在Hibernate-Doclet 中,对应的就是 @hibernate.discriminator 标识和@hibernate.class 以及@hibernate.subclass 的 discriminator-value 属性。 典型场景: /** *
* @hibernate.class * table="TUser" * dynamic-update="true" * dynamic-insert="true" * * @hibernate.discriminator column="user_type" type="integer" */ public class TUser implements Serializable {
……
}
根类TUser 中,通过@hibernate.discriminator 指定了以"user_type"字段 作为识别字段。 /**
* @hibernate.subclass * discriminator-value="1" */ public class SysAdmin extends TUser {
……
}
/**
* @hibernate.subclass * discriminator-value="2" */ public class SysOperator extends TUser {
……
}
SysAdmin 和SysOperator 均继承自TUser ,其discriminator-value 分别设置 为"1"和"2",运行期Hibernate 在读取t_user 表数据时,会根据其user_type 字段进行 判断,如果是1 的话则映射到SysAdmin 类,如果是2 映射到SysOperator 类。 上例中,描述SysAdmin 和SysOperator 时,我们引入了一个Tag :
@hibernate.subclass,顾名思义,@hibernate.subclass 与@hibernate.class 不同之处就在于,@hibernate.subclass 描述的是一个子类,实际上,这两个Tag 除去名称不同外,并没有什么区别。 2. Method 层面: 1) @hibernate.id 描述POJO 中关键字段与数据库表主键之间的映射关系。 参数描述类型必须 column 主键字段名 默认值:当前类名 Text N type 字段类型。 Hibernate 总是使用对象型数据类型作 为字段类型,如int 对应Integer,因此 这里将id 设为基本类型[如int] 以避免对 象创建的开销的思路是没有实际意义的, 即使这里设置为基本类型,Hibernate 内 部还是会使用对象型数据对其进行处理, 只是返回数据的时候再转换为基本类型 而已。 Text N length 字段长度Text N unsaved-value 用于对象是否已经保存的判定值。 详见“ 数据访问”章节的相关讨论。 Text N generator-class 主键产生方式(详见Hibernate Quick Start 中关于MiddleGen 的相关说明) 取值可为下列值中的任意一个: assigned hilo seqhilo increment identity sequence native uuid.hex uuid.string foreign Text Y 2) @hibernate.property
描述POJO 中属性与数据库表字段之间的映射关系。 参数描述类型必须 column 数据库表字段名 默认值:当前类名 Text N type 字段类型Text N length 字段长度Text N not-null 字段是否允许为空Bool N unique 字段是否唯一(是否允许重复值)Bool N insert Insert 操作时是否包含本字段数据 默认:true Bool N update Update 操作时是否包含本字段数据 默认:true Bool N 典型场景: /**
* @hibernate.property * column="name" * length="50" * not-null="true" * * @return String */ public String getName() { return this.name;
}
注意:在编写代码的时候请,对将POJO的getter/setter 方法设定为public,如果 设定为private,Hibernate将无法对属性的存取进行优化,只能转而采用传统的反射机制 进行操作,这将导致大量的性能开销(特别是在1.4之前的Sun JDK版本以及IBM JDK中, 反射所带来的系统开销相当可观)。 包含XDoclet Tag的代码必须由xdoclet程序进行处理以生成对应的映射文件, xdoclet的处理模块可通过ant进行加载,下面是一个简单的hibernate xdoclet的ant 构建脚本(注意实际使用时需要根据实际情况对路径和CLASSPATH设定进行调整): <?xml version="1.0"?>
<project name="Hibernate" default="hibernate" basedir=".">
<property name="xdoclet.lib.home"
value="C:\xdoclet-1.2.1\lib"/>
<target name="hibernate" depends="" description="Generates Hibernate class descriptor files.">
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.HibernateDocletTask">
<classpath> <fileset dir="${xdoclet.lib.home}"> <include name="*.jar"/> </fileset> </classpath>
</taskdef>
<hibernatedoclet destdir="./src/" excludedtags="@version,@author,@todo" force="true" verbose="true" mergedir=".">
<fileset dir="./src/"> <include name="**/hibernate/sample/*.java"/> </fileset>
<hibernate version="2.0"/>
</hibernatedoclet> </target>
</project>
除了上面我们介绍的Hibernate Doclet Tag,其他还有: Class层面; @hibernate.cache @hibernate.jcs-cache @hibernate.joined-subclass @hibernate.joined-subclass-key @hibernate.query
Method层面 @hibernate.array @hibernate.bag @hibernate.collection-cache @hibernate.collection-composite-element @hibernate.collection-element @hibernate.collection-index @hibernate.collection-jcs-cache @hibernate.collection-key @hibernate.collection-key-column @hibernate.collection-many-to-many @hibernate.collection-one-to-many @hibernate.column @hibernate.component @hibernate.generator-param @hibernate.index-many-to-many
@hibernate.list @hibernate.many-to-one @hibernate.map @hibernate.one-to-one @hibernate.primitive-array @hibernate.set @hibernate.timestamp @hibernate.version
具体的Tag描述请参见XDoclet官方网站提供的Tag说明1。下面的Hibernate高级特性介 绍中,我们也将涉及到这些Tag的实际使用。 http://xdoclet.sourceforge.net/xdoclet/tags/hibernate-tags.html
编后赘言 Hibernate 是一个优秀的ORM 实现,不过请注意,它只是一个ORM 实现而已,也不能保证是最优秀的。 笔者使用过的ORM 实现中,Apache OJB 、Oracle TopLink 、IBatis 和Jaxor 都给笔者留下了深刻映像,是否选择Hibernate 作为持久层实现, 需要结合实际情况考虑(在很多情况下,比如对遗留系统的改造项目中、 ibatis 可能更加合适)。 合理的设计,冷静的取舍是考量一个系统架构师功底的最实际的标 准。常在网上看到对Hibernate 以及其他一些项目或者框架标准的狂热 鼓吹,不禁想起自己三年前逢项目必定为EJB 摇旗呐喊的样子。 设计需要用时间来沉淀,建筑、服装都是如此,软件产品也一样……
描述: Hibernate 开发指南
附件:
Hibernate_DEV_GUIDE.rar (462 K)
|
一共有 1 条评论