WARNING! This is a translated version of the English Hibernate reference documentation. The translated version might not be up to date! However, the differences should only be very minor. Consult the English reference documentation if you are missing information or encounter a translation error. If you like to contribute to a particular translation, contact us on the Hibernate developer mailing list.
Translator(s): RedSaga Translate Team 满江红翻译团队 <caoxg@yahoo.com>
在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。Hibernate是一个面向Java环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。
Hibernate不仅仅管理Java类到数据库表的映射(包括Java数据类型到SQL数据类型的映射),还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和JDBC处理数据的时间。
Hibernate的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。对于以数据为中心的程序来说,它们往往只在数据库中使用存储过程来实现商业逻辑,Hibernate可能不是最好的解决方案;对于那些在基于Java的中间层应用中,它们实现面向对象的业务模型和商业逻辑的应用,Hibernate是最有用的。不管怎样,Hibernate一定可以帮助你消除或者包装那些针对特定厂商的SQL代码,并且帮你把结果集从表格式的表示形式转换到一系列的对象去。
如果你对Hibernate和对象/关系数据库映射还是个新手,或者甚至对Java也不熟悉,请按照下面的步骤来学习。
-
阅读这个30分钟就可以结束的第 1 章 在Tomcat中快速上手,它使用Tomcat。
-
阅读第 2 章 Hibernate入门 ,这是一篇较长的指南,包含详细的逐步指导。
-
阅读第 3 章 体系结构(Architecture)来理解Hibernate可以使用的环境。
-
查看Hibernate发行包中的eg/目录,里面有一个简单的独立运行的程序。把你的JDBC驱动拷贝到lib/目录下,修改一下src/hibernate.properties,指定其中你的数据库的信息。进入命令行,切换到你的发行包的目录,输入ant eg(使用了Ant),或者在Windows操作系统中使用build eg。
-
把这份参考文档作为你学习的主要信息来源。
-
在Hibernate 的网站上可以找到经常提问的问题与解答(FAQ)。
-
在Hibernate网站上还有第三方的演示、示例和教程的链接。
-
Hibernate网站的“社区(Community Area)”是讨论关于设计模式以及很多整合方案(Tomcat, JBoss AS, Struts, EJB,等等)的好地方。
如果你有问题,请使用Hibernate网站上链接的用户论坛。我们也提供一个JIRA问题追踪系统,来搜集bug报告和新功能请求。如果你对开发Hibernate有兴趣,请加入开发者的邮件列表。(Hibernate网站上的用户论坛有一个中文版面,JavaEye也有Hibernate中文版面,您可以在那里交流问题与经验。)
商业开发、产品支持和Hibernate培训可以通过JBoss Inc.获得。(请查阅:http://www.hibernate.org/SupportTraining/)。 Hibernate是一个专业的开放源代码项目(Professional Open Source project),也是JBoss Enterprise Middleware System(JEMS),JBoss企业级中间件系统的一个核心组件。
本文档的翻译是在网络上协作进行的,也会不断根据Hibernate的升级进行更新。提供此文档的目的是为了减缓学习Hibernate的坡度,而非代替原文档。我们建议所有有能力的读者都直接阅读英文原文。若您对翻译有异议,或发现翻译错误,敬请不吝赐教,报告到如下email地址:cao at redsaga.com
Hibernate版本3的翻译由满江红翻译团队(RedSaga Translate Team)集体进行,这也是一次大规模网络翻译的试验。在不到20天的时间内,我们完成了两百多页文档的翻译,这一成果是通过十几位网友集体努力完成的。通过这次翻译,我们也有了一套完整的流程,从初译、技术审核一直到文字审核、发布。我们的翻译团队还会继续完善我们的翻译流程,并翻译其他优秀的Java开源资料,敬请期待。
表 1. Hibernate v3翻译团队
| 序号 |
标题 |
中文标题 |
翻译 |
审校 |
| #1 |
Quickstart with Tomcat |
在Tomcat中快速上手 |
曹晓钢 |
zoujm |
| #2 |
Architecture |
体系结构 |
Hilton(BJUG) |
厌倦发呆 |
| #3 |
Configuration |
配置 |
Goncha |
mochow |
| #4 |
Persistent Classes |
持久化类 |
曹晓钢 |
mochow |
| #5 |
Basic O/R Mapping |
对象/关系数据库映射基础(上) |
moxie |
Kingfish |
| |
|
对象/关系数据库映射基础(下) |
inter_dudu |
vincent |
| #6 |
Collection Mapping |
集合类映射 |
曹晓钢 |
robbin |
| #7 |
Association Mappings |
关联关系映射 |
Robbin |
devils.advocate |
| #8 |
Component Mapping |
组件映射 |
曹晓钢 |
Robbin |
| #9 |
Inheritance Mappings |
继承映射 |
morning(BJUG) |
mochow |
| #10 |
Working with objects |
与对象共事 |
程广楠 |
厌倦发呆 |
| #11 |
Transactions And Concurrency |
事务和并发 |
Robbin |
mochow |
| #12 |
Interceptors and events |
继承映射 |
七彩狼(BJUG) |
厌倦发呆 |
| #13 |
Batch processing |
批量处理 |
Kingfish(BJUG) |
厌倦发呆 |
| #14 |
HQL: The Hibernate Query Language |
HQL: Hibernate查询语言 |
郑浩(BJUG) |
zhengshuai |
| #15 |
Criteria Queries |
条件查询 |
nemo(BJUG) |
zhengshuai |
| #16 |
Native SQL |
Native SQL查询 |
似水流年 |
zoujm |
| #17 |
Filters |
过滤数据 |
冰云(BJUG) |
Goncha |
| #18 |
XML Mapping |
XML映射 |
edward(BJUG) |
Goncha |
| #19 |
Improving performance |
性能提升 |
Wangjinfeng |
Robbin |
| #20 |
Toolset Guide |
工具箱指南 |
曹晓钢 |
Robbin |
| #21 |
Example: Parent/Child |
示例:父子关系 |
曹晓钢 |
devils.advocate |
| #22 |
Example: Weblog Application |
示例:Weblog 应用程序 |
曹晓钢 |
devils.advocate |
| #23 |
Example: Various Mappings |
示例:多种映射 |
shidu(BJUG) |
冰云 |
| #24 |
Best Practices |
最佳实践 |
曹晓钢 |
冰云 |
关于我们
- 满江红.开源, http://www.redsaga.com
-
从成立之初就致力于Java开放源代码在中国的传播与发展,与国内多个Java团体及出版社有深入交流。坚持少说多做的原则,目前有两个团队,“OpenDoc团队”与“翻译团队”,本翻译文档即为翻译团队作品。OpenDoc团队已经推出包括Hibernate、iBatis、Spring、WebWork的多份开放文档,并于2005年5月在Hibernate开放文档基础上扩充成书,出版了原创书籍:《深入浅出Hibernate》,本书400余页,适合各个层次的Hibernate用户。(http://www.redsaga.com/hibernate_book.html)敬请支持。
- 北京Java用户组, http://www.bjug.org
-
Beiing Java User Group,民间技术交流组织,成立于2004年6月。以交流与共享为宗旨,每两周举行一次技术聚会活动。BJUG的目标是,通过小部分人的努力,形成一个技术社群,创建良好的交流氛围,并将新的技术和思想推广到整个IT界,让我们共同进步。
- Java视线, http://www.javaeye.com
-
Java视线在是Hibernate中文论坛(http://www.hibernate.org.cn,Hibernate中文论坛是中国最早的Hibernate专业用户论坛,为Hibernate在中国的推广做出了巨大的贡献)基础上发展起来的Java深度技术网站,目标是成为一个高品质的,有思想深度的、原创精神的Java技术交流网站,为软件从业人员提供一个自由的交流技术,交流思想和交流信息的平台。
Hibernate英文文档属于Hibernate发行包的一部分,遵循LGPL协议。本翻译版本同样遵循LGPL协议。参与翻译的译者一致同意放弃除署名权外对本翻译版本的其它权利要求。
您可以自由链接、下载、传播此文档,或者放置在您的网站上,甚至作为产品的一部分发行。但前提是必须保证全文完整转载,包括完整的版权信息和作译者声明,并不能违反LGPL协议。这里“完整”的含义是,不能进行任何删除/增添/注解。若有删除/增添/注解,必须逐段明确声明那些部分并非本文档的一部分。
这份教程描述如何在Apache Tomcat servlet容器中为web应用程序配置Hibernate 3.0(我们使用Tomcat 4.1版本,与5.0版本差别很小)。Hibernate在大多数主流J2EE应用服务器 的运行环境中都可以工作良好,甚至也可以在独立Java应用程序中使用。在本教程中使用的示例数据库系统是PostgreSQL 7.4,只需要修改Hibernate SQL语言配置与连接属性,就可以 很容易的支持其他数据库了。
第一步,我们必须拷贝所有需要的库文件到Tomcat安装目录中。在这篇教程中,我们使用一个独立的web Context配置(webapps/quickstart)。我们确认全局库文件(TOMCAT/common/lib)和本web应用程序上下文的路径(对于jar来说是webapps/quickstart/WEB-INF/lib,对于class文件来说是webapps/quickstart/WEB-INF/classes)能够被类装载器检索到。我们把这两个类装载器级别分别称做全局类路径(global classpath)和上下文类路径(context classpath)。
现在,把这些库文件copy到两个类路径去:
-
把数据库需要的JDBC驱动文件拷贝到全局类路径,这是tomcat捆绑的DBCP连接池所需要的。Hibernate使用JDBC连接数据库方式执行SQL语句,所以你要么提供外部连接池中的连接给Hibernate,或者配置Hibernate自带的连接池(C3PO,Proxool)。对于本教程来说,把pg74jdbc3.jar库文件(支持PostgreSQL 7.4和JDK 1.4)到全局类装载路径下即可。如果你希望使用其他的数据库,拷贝其相应的JDBC 驱动文件)。
-
永远不要拷贝任何其他东西到Tomcat的全局类路径下,否则你可能在使用其他一些工具上遇到麻烦,比如log4j, commons-logging等等。 一定要让每个web应用程序使用自己的上下文类路径,就是说把你自己需要的类库拷贝到WEB-INF/lib下去,把配置文件configuration/property等配置文件拷贝到WEB-INF/classes下面去。这两个目录都是当前程序缺省的上下文类路径。
-
Hibernate本身打包成一个JAR类库。将hibernate3.jar文件拷贝到程序的上下文类路径下,和你应用程序的其他库文件放一起。在运行时,Hibernate还需要一些第三方类库,它们在Hibernate发行包的lib/目录下。参见表 1.1 “ Hibernate 第三方类库 ”。把所需要的第三方库文件也拷贝到上下文类路径下。
表 1.1. Hibernate 第三方类库
| 类库 |
描述 |
| antlr (必需) |
Hibernate使用ANTLR来产生查询分析器,这个类库在运行环境下时也是必需的。 |
| dom4j (必需) |
Hibernate使用dom4j解析XML配置文件和XML映射元文件。 |
| CGLIB ,asm(必需) |
Hibernate在运行时使用这个代码生成库增强类(与Java反射机制联合使用)。 |
| Commons Collections, Commons Logging (必需) |
Hibernat使用Apache Jakarta Commons项目提供的多个工具类库。 |
| EHCache (必需) |
Hibernate可以使用不同cache缓存工具作为二级缓存。EHCache是缺省的cache缓存工具。 |
| Log4j (可选) |
Hibernate使用Commons Logging API,它也可以依次使用Log4j作为底层实施log的机制。如果上下文类目录中存在Log4j库,则Commons Logging使用Log4j和并它在上下文类路径中寻找的log4j.properties文件。你可以使用在Hibernate发行包中包含中的那个示例Log4j的配置文件。这样,把log4j.jar和它的配置文件(位于src/目录中)拷贝到你的上下文类路径下,就可以在后台看到底程序如何运行的。 |
| 其他文件是不是必需的? |
请察看Hibernate发行包中的 lib/README.txt文件,这是一个Hibernate发行包中附带的第三方类库的列表,他们总是保持最新的。你可以在那里找到所有必需或者可选的类库(注意:其中的"buildtime required"指的是编译Hibernate时所需要而非编译你自己的程序所必需的类库)。 |
接下来我们来配置在Tomcat和Hibernate中共用的数据库连接池。也就是说Tomcat会提供经过池处理的JDBC连接(用它内置的DBCP连接池),Hibernate通过JNDI方式来请求获得JDBC连接。作为替代方案,你也可以让Hibernate自行管理连接池。Tomcat把连接池绑定到JNDI,我们要在Tomcat的主配置文件(TOMCAT/conf/server.xml)中加一个资源声明:
<Context path="/quickstart" docBase="quickstart">
<Resource name="jdbc/quickstart" scope="Shareable" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/quickstart">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- DBCP database connection settings -->
<parameter>
<name>url</name>
<value>jdbc:postgresql://localhost/quickstart</value>
</parameter>
<parameter>
<name>driverClassName</name><value>org.postgresql.Driver</value>
</parameter>
<parameter>
<name>username</name>
<value>quickstart</value>
</parameter>
<parameter>
<name>password</name>
<value>secret</value>
</parameter>
<!-- DBCP connection pooling options -->
<parameter>
<name>maxWait</name>
<value>3000</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>100</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>10</value>
</parameter>
</ResourceParams>
</Context>
我们在这个例子中要配置的上下文叫做quickstart,它位于TOMCAT/webapp/quickstart目录下。如果要访问这个应用程序,在你的浏览器中输入http://localhost:8080/quickstart就可以了(当然,在后面加上在你的web.xml文件中配置好你的servlet)。你现在可以创建一个只含有空process()的简单servlet了。
Tomcat现在通过JNDI的方式:java:comp/env/jdbc/quickstart来提供连接。如果你在配置连接池遇到问题,请查阅Tomcat文档。如果你遇到了JDBC驱动所报的exception出错信息,请在没有Hibernate的环境下,先测试JDBC连接池本身是否配置正确。Tomcat和JDBC的配置教程可以在Web上查到。
下一步就是配置Hibernate。首先Hibernate必须知道它如何获得JDBC连接,在这里我们使用基于XML格式的Hibernate配置文件。当然使用properties文件的进行配置,但缺少一些XML语法的特性。这个XML配置文件必须放在上下文类路径(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-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.datasource">java:comp/env/jdbc/quickstart</property>
<property name="show_sql">false</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- Mapping files -->
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>
在这里我们关闭了SQL命令的log,同时告诉Hibernate使用哪种SQL数据库用语(Dialet),以及如何得到JDBC连接(通过Tomcat声明绑定的JNDI地址)。Dialet是必需配置的,因为不同的数据库都和"SQL标准"有一些出入。不用担心,Hibernate会替你处理这些差异,Hibernate支持所有主流的商业和开放源代码数据库。
SessionFactory是Hibernate的一个概念,表示对应一个数据存储源。通过创建多个XML配置文件并在你的程序中创建多个Configuration和SessionFactory对象,就可以支持多个数据库了。
在hibernate.cfg.xml中的最后一个元素声明了Cat.hbm.xml,这是一个Hibernate XML映射文件,对应于持久化类Cat。这个文件包含了把Cat POJO类映射到数据库表(或多个数据库表)的元数据。我们稍后就回来看这个文件。下一步让我们先编写这个POJO类,然后在声明它的映射元数据。
Hibernate使用简单的Java对象(Plain Old Java Objects ,就是POJOs,有时候也称作Plain Ordinary Java Objects)这种编程模型来进行持久化。一个POJO很像JavaBean,通过getter和setter方法访问其属性,对外则隐藏了内部实现的细节(假若需要的话,Hibernate也可以直接访问其属性字段)。
package org.hibernate.examples.quickstart;
public class Cat {
private String id;
private String name;
private char sex;
private float weight;
public Cat() {
}
public String getId() {
return id;
}
private void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
}
Hibernate对属性使用的类型不加任何限制。所有的Java JDK类型和原始类型(比如String,char和Date)都可以被映射,也包括Java 集合(Java collections framework)中的类。你可以把它们映射成为值,值集合,或者与其他实体类相关联。id是一个特殊的属性,代表了这个类的数据库标识符(主键),对于类似于Cat这样的实体类我们强烈建议使用。Hibernate也可以使用内部标识符,但这样我们会失去一些程序架构方面的灵活性。
持久化类不需要实现什么特别的接口,也不需要从一个特别的持久化根类继承下来。Hibernate也不需要使用任何编译期处理,比如字节码增强操作,它独立的使用Java反射机制和运行时类增强(通过CGLIB)。所以不依赖于Hibernate,我们就可以把POJO的类映射成为数据库表。
Cat.hbm.xml映射文件包含了对象/关系映射(O/R Mapping)所需的元数据。元数据包含持久化类的声明和属性到数据库的映射(指向字段和其他实体的外键关联)。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.hibernate.examples.quickstart.Cat" table="CAT">
<!-- A 32 hex character is our surrogate key. It's automatically
generated by Hibernate with the UUID pattern. -->
<id name="id" type="string" unsaved-value="null" >
<column name="CAT_ID" sql-type="char(32)" not-null="true"/>
<generator class="uuid.hex"/>
</id>
<!-- A cat has to have a name, but it shouldn' be too long. -->
<property name="name">
<column name="NAME" length="16" not-null="true"/>
</property>
<property name="sex"/>
<property name="weight"/>
</class>
</hibernate-mapping>
每个持久化类都应该有一个标识属性(实际上,这个类只代表实体,而不是独立的值类型类,后者会被映射称为实体对象中的一个组件)。这个属性用来区分持久化对象:如果catA.getId().equals(catB.getId())结果是true的话,这两个Cat就是相同的。这个概念称为数据库标识。Hiernate附带了几种不同的标识符生成器,用于不同的场合(包括数据库本地的顺序(sequence)生成器、hi/lo高低位标识模式、和程序自己定义的标识符)。我们在这里使用UUID生成器(只在测试时建议使用,如果使用数据库自己生成的整数类型的键值更好),并指定CAT表中的CAT_ID字段(作为表的主键)存放生成的标识值。
Cat的其他属性都映射到同一个表的字段。对name属性来说,我们把它显式地声明映射到一个数据库字段。如果数据库schema是通过由映射声明使用Hibernate的SchemaExport工具自动生成的(作为SQL DDL指令)的话,这就特别有用。所有其它的属性都用Hibernate的默认值映射,大多数情况你都会这样做。数据库中的CAT表看起来是这样的:
Column | Type | Modifiers
--------+-----------------------+-----------
cat_id | character(32) | not null
name | character varying(16) | not null
sex | character(1) |
weight | real |
Indexes: cat_pkey primary key btree (cat_id)
你现在可以在你的数据库中手工创建这个表了,如果你需要使用hbm2ddl工具把这个步骤自动化,请参阅第 21 章 工具箱指南。这个工具能够创建完整的SQL DDL,包括表定义,自定义的字段类型约束,惟一约束和索引。
我们现在可以开始Hibernate的Session了。它是一个持久化管理器,我们通过它来从数据库中存取Cat。首先,我们要从SessionFactory中获取一个Session(Hibernate的工作单元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
通过对configure()的调用来装载hibernate.cfg.xml配置文件,并初始化成一个Configuration实例。 在创建 SessionFactory之前(它是不可变的),你可以访问Configuration来设置其他属性(甚至修改映射的元数据)。我们应该在哪儿创建SessionFactory,在我们的程序中又如何访问它呢? SessionFactory通常只是被初始化一次,比如说通过一个load-on-startup servlet的来初始化。这意味着你不应该在serlvet中把它作为一个实例变量来持有,而应该放在其他地方。进一步的说,我们需要使用单例(Singleton)模式,我们才能更容易的在程序中访问SessionFactory。下面的方法就同时解决了两个问题:对SessionFactory的初始配置与便捷使用。
我们实现一个HibernateUtil辅助类:
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
log.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession() {
Session s = (Session) session.get();
// Open a new Session, if this Thread has none yet
if (s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() {
Session s = (Session) session.get();
if (s != null)
s.close();
session.set(null);
}
}
这个类不但在它的静态初始器中使用了SessionFactory,还使用了一个ThreadLocal变量来保存Session做为当前工作线程。在你使用这个辅助类之前,请确保你理解了thread-local变量这个Java概念。你可以在CaveatEmptor(http://caveatemptor.hibernate.org/)上找到一个更加复杂和强大的 HibernateUtil。
SessionFactory是安全线程,可以由很多线程并发访问并获取到Sessions。单个Session不是安全线程对象,它只代表与数据库之间的一次操作。Session通过SessionFactory获得并在所有的工作完成后关闭。在你servlet的process()中可以象是这么写的(省略了异常情况处理):
Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
Cat princess = new Cat();
princess.setName("Princess");
princess.setSex('F');
princess.setWeight(7.4f);
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
在一个Session中,每个数据库操作都是在一个事务(transaction)中进行的,这样就可以隔离开不同的操作(甚至包括只读操作)。我们使用Hibernate的Transaction API来从底层的事务策略中(本例中是JDBC事务)脱身出来。这样,我们就不需要更改任何源代码,就可以把我们的程序部署到一个由容器管理事务的环境中去(使用JTA)。
这样你就可以随心所欲的多次调用HibernateUtil.currentSession();,你每次都会得到同一个当前线程的Session。不管是在你的servlet代码中,或者在servlet filter中还是在HTTP结果返回之前,你都必须确保这个Session在你的数据库访问工作完成后关闭。这样做还有一个好处就是可以容易的使用延迟装载(lazy initialization):Session在渲染view层的时候仍然打开着的,所以你在遍历当前对象图的时候可以装载所需的对象。
Hibernate有不同的方法用来从数据库中取回对象。最灵活的方式就是使用Hibernate查询语言(HQL),这是一种容易学习的语言,是对SQL的面向对象的强大扩展。
Transaction tx= session.beginTransaction();
Query query = session.createQuery("select c from Cat as c where c.sex = :sex");
query.setCharacter("sex", 'F');
for (Iterator it = query.iterate(); it.hasNext();) {
Cat cat = (Cat) it.next();
out.println("Female Cat: " + cat.getName() );
}
tx.commit();
Hibernate也提供一种面向对象的按条件查询API,可以执行简洁安全类型的查询。当然,Hibernate在所有与数据库的交互中都使用PrepatedStatement和参数绑定。你也可以使用Hibernate的直接SQL查询特性,或者在特殊情况下从Session获取一个原始的JDBC连接。
在这个短小的教程中,我们对Hibernate浅尝即止。请注意我们没有在例子中包含任何servlet相关代码。你必须自行编写servlet,并插入适合你的Hibernate代码。
请记住Hibernate作为一个数据库访问层,是与你的程序紧密相关的。通常情况下,所有其他层次都依赖持久机制。请确信你理解了这种设计的内涵。
若希望学习更复杂的例子,请参阅http://caveatemptor.hibernate.org/ 。在 http://www.hibernate.org/Documentation 也可以得到其他教程的链接。
本章是面向Hibernate初学者的一个介绍教程。我们将使用容易理解的方式,开发一个使用驻留内存式(in-memory)数据库的简单命令行程序。
本教程是面向Hibernate初学者,但是需要一定的Java和SQL知识。 它在Michael Goegl所写的一个教程的基础上完成的。我们使用的第三方库文件是支持JDK 1.4和5.0。如果你要使用JDK1.3,可能会需要其它的库。
2.2. 第一部分 - 第一个Hibernate程序
首先我们将创建一个简单的控制台(console-based)Hibernate程序。我们使用内置数据库(in-memory database) (HSQL DB),所以我们不必安装任何数据库服务器。
让我们假设我们希望有一个小程序可以保存我们希望关注的事件(Event)和这些事件的信息。 (译者注:在本教程的后面部分,我们将直接使用Event而不是它的中文翻译“事件”,以免混淆。)
我们做的第一件事是建立我们的开发目录,并把所有需要用到的Java库文件放进去。 从Hibernate网站的下载页面下载Hibernate分发版本。 解压缩包并把/lib下面的所有库文件放到我们新的开发目录下面的/lib目录下面。 看起来就像这样:
.
+lib
antlr.jar
cglib-full.jar
asm.jar
asm-attrs.jars
commons-collections.jar
commons-logging.jar
ehcache.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar
This is the minimum set of required libraries (note that we also copied hibernate3.jar, the main archive) for Hibernate. See the README.txt file in the lib/ directory of the Hibernate distribution for more information about required and optional third-party libraries. (Actually, Log4j is not required but preferred by many developers.) 这个是Hibernate运行所需要的最小库文件集合(注意我们也拷贝了Hibernate3.jar,这个是最重要的库)。 可以在Hibernate分发版本的lib/目录下查看README.txt,以获取更多关于所需和可选的第三方库文件信息 (事实上,Log4j并不是必须的库文件但是许多开发者都喜欢用它)。
接下来我们创建一个类,用来代表那些我们希望储存在数据库里面的event.
我们的第一个持久化类是 一个简单的JavaBean class,带有一些简单的属性(property)。 让我们来看一下代码:
import java.util.Date;
public class Event {
private Long id;
private String title;
private Date date;
Event() {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
你可以看到这个class对属性(property)的存取方法(getter and setter method) 使用标准的JavaBean命名约定,同时把内部字段(field)隐藏起来(private visibility)。 这个是个受推荐的设计方式,但并不是必须这样做。 Hibernate也可以直接访问这些字段(field),而使用访问方法(accessor method)的好处是提供了程序重构的时候健壮性(robustness)。
id 属性(property) 为一个Event实例提供标识属性(identifier property)的值- 如果我们希望使用Hibernate的所有特性,那么我们所有的持久性实体类(persistent entity class)(这里也包括一些次要依赖类) 都需要一个标识属性(identifier property)。而事实上,大多数应用程序(特别是web应用程序)都需要识别特定的对象,所以你应该 考虑使用标识属性而不是把它当作一种限制。然而,我们通常不会直接操作一个对象的标识符(identifier), 因此标识符的setter方法应该被声明为私有的(private)。这样当一个对象被保存的时候,只有Hibernate可以为它分配标识符。 你会发现Hibernate可以直接访问被声明为public,private和protected等不同级别访问控制的方法(accessor method)和字段(field)。 所以选择哪种方式来访问属性是完全取决于你,你可以使你的选择与你的程序设计相吻合。
所有的持久类(persistent classes)都要求有无参的构造器(no-argument constructor); 因为Hibernate必须要使用Java反射机制(Reflection)来实例化对象。构造器(constructor)的访问控制可以是私有的(private), 然而当生成运行时代理(runtime proxy)的时候将要求使用至少是package级别的访问控制,这样在没有字节码编入 (bytecode instrumentation)的情况下,从持久化类里获取数据会更有效率一些。
我们把这个Java源代码文件放到我们的开发目录下面一个叫做src的目录里。 这个目录现在应该看起来像这样:
.
+lib
<Hibernate and third-party libraries>
+src
Event.java
在下一步里,我们将把这个持久类(persisten class)的信息通知Hibernate
Hibernate需要知道怎样去加载(load)和存储(store)我们的持久化类的对象。这里正是Hibernate映射文件(mapping file)发挥作用的地方。 映射文件告诉Hibernate它应该访问数据库里面的哪个表(table)和应该使用表里面的哪些字段(column)。
一个映射文件的基本结构看起来像这样:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
[...]
</hibernate-mapping>
注意Hibernate的DTD是非常复杂的。 你可以在你的编辑器或者IDE里面使用它来自动提示并完成(auto-completion)那些用来映射的XML元素(element)和属性(attribute)。 你也可以用你的文本编辑器打开DTD-这是最简单的方式来浏览所有元素和参数,查看它们的缺省值以及它们的注释,以得到一个整体的概观。 同时也要注意Hibernate不会从web上面获取DTD文件,虽然XML里面的URL也许会建议它这样做,但是Hibernate会首先查看你的程序的classpath。 DTD文件被包括在hibernate3.jar,同时也在Hibernate分发版的src/路径下。
在以后的例子里面,我们将通过省略DTD的声明来缩短代码长度。但是显然,在实际的程序中,DTD声明是必须的。
在两个hibernate-mapping标签(tag)中间, 我们包含了一个 class元素(element)。所有的持久性实体类(persistent entity classes)(再次声明, 这里也包括那些依赖类,就是那些次要的实体)都需要一个这样的映射,来映射到我们的SQL database。
<hibernate-mapping>
<class name="Event" table="EVENTS">
</class>
</hibernate-mapping>
我们到现在为止做的一切是告诉Hibernate怎样从数据库表(table)EVENTS里持久化和 加载Event类的对象,每个实例对应数据库里面的一行。现在我们将继续讨论有关唯一标识属性(unique identifier property)的映射。 另外,我们不希望去考虑怎样产生这个标识属性,我们将配置Hibernate的标识符生成策略(identifier generation strategy)来产生代用主键。
<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
</class>
</hibernate-mapping>
id元素是标识属性(identifer property)的声明, name="id" 声明了Java属性(property)的名字 - Hibernate将使用getId()和setId()来访问它。 字段参数(column attribute)则告诉Hibernate我们使用EVENTS表的哪个字段作为主键。 嵌套的generator元素指定了标识符的生成策略 - 在这里我们使用increment,这个是非常简单的在内存中直接生成数字的方法,多数用于测试(或教程)中。 Hibernate同时也支持使用数据库生成(database generated),全局唯一性(globally unique)和应用程序指定(application assigned) (或者你自己为任何已有策略所写的扩展) 这些方式来生成标识符。
最后我们还必须在映射文件里面包括需要持久化属性的声明。缺省的情况下,类里面的属性都被视为非持久化的:
<hibernate-mapping>
<class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="increment"/>
</id>
<property name="date" type="timestamp" column="EVENT_DATE"/>
<property name="title"/>
</class>
</hibernate-mapping>
和id元素类似,property元素的name参数 告诉Hibernate使用哪个getter和setter方法。
为什么date属性的映射包括column参数,但是title却没有? 当没有设定column参数的时候,Hibernate缺省使用属性名作为字段(column)名。对于title,这样工作得很好。 然而,date在多数的数据库里,是一个保留关键字,所以我们最好把它映射成另外一个名字。
下一件有趣的事情是title属性缺少一个type参数。 我们声明并使用在映射文件里面的type,并不像我们假想的那样,是Java data type, 同时也不是SQL database type。这些类型被称作Hibernate mapping types, 它们把数据类型从Java转换到SQL data types。如果映射的参数没有设置的话,Hibernate也将尝试去确定正确的类型转换和它的映射类型。 在某些情况下这个自动检测(在Java class上使用反射机制)不会产生你所期待或者 需要的缺省值。这里有个例子是关于date属性。Hibernate无法知道这个属性应该被映射成下面这些类型中的哪一个: SQL date,timestamp,time。 我们通过声明属性映射timestamp来表示我们希望保存所有的关于日期和时间的信息。
这个映射文件(mapping file)应该被保存为Event.hbm.xml,和我们的EventJava 源文件放在同一个目录下。映射文件的名字可以是任意的,然而hbm.xml已经成为Hibernate开发者社区的习惯性约定。 现在目录应该看起来像这样:
.
+lib
<Hibernate and third-party libraries>
+src
Event.java
Event.hbm.xml
我们继续进行Hibernate的主要配置。
我们现在已经有了一个持久化类和它的映射文件,是时候配置Hibernate了。在我们做这个之前,我们需要一个数据库。 HSQL DB,一个java-based内嵌式SQL数据库(in-memory SQL Database),可以从HSQL DB的网站上下载。 实际上,你仅仅需要下载/lib/目录中的hsqldb.jar。把这个文件放在开发文件夹的lib/目录里面。
在开发目录下面创建一个叫做data的目录 - 这个是HSQL DB存储它的数据文件的地方。
Hibernate是你的程序里连接数据库的那个应用层,所以它需要连接用的信息。连接(connection)是通过一个也由我们配置的JDBC连接池(connection pool)。 Hibernate的分发版里面包括了一些open source的连接池,但是我们已经决定在这个教程里面使用内嵌式连接池。 如果你希望使用一个产品级的第三方连接池软件,你必须拷贝所需的库文件去你的classpath并使用不同的连接池设置。
为了配置Hibernate,我们可以使用一个简单的hibernate.properties文件, 或者一个稍微复杂的hibernate.cfg.xml,甚至可以完全使用程序来配置Hibernate。 多数用户喜欢使用XML配置文件:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DT