Table of Contents
-
前言 1.
在Tomcat中快速上手
-
-
1.1.
开始Hibernate之旅 1.2.
第一个可持久化类 1.3.
映射cat 1.4.
与猫同乐 1.5.
结语
2.
体系结构
-
-
2.1.
总览 2.2.
持久化对象标识(Persistent Object Identity ) 2.3.
JMX集成 2.4.
JCA支持
3.
SessionFactory配置
-
-
3.1.
可编程配置方式 3.2.
获取SessionFactory 3.3.
用户自行提供JDBC连接 3.4.
Hibernate提供的JDBC连接 3.5.
其它配置属性
-
-
3.5.1.
SQL Dialects SQL 方言 3.5.2.
外连接抓取(Outer Join Fetching ) 3.5.3.
二进制流 3.5.4.
在控制台记录SQL 3.5.5.
自定义 ConnectionProvider 3.5.6.
常用数据库属性 3.5.7.
自定义CacheProvider 3.5.8.
事务策略 3.5.9.
绑定SessionFactory到JNDI 3.5.10.
查询语言替换
3.6.
XML配置文件 3.7.
Logging 3.8.
NamingStrategy
4.
持久化类(Persistent Classes)
-
-
-
4.1.
简单示例
-
-
4.1.1.
为持久化字段声明访问器(accessors)和是否可变的标志(mutators) 4.1.2.
实现一个默认的构造方法(constructor) 4.1.3.
提供一个标识属性(identifier property)(可选) 4.1.4.
建议使用不是final的类 (可选)
4.2.
继承(Inheritance ) 4.3.
持久化生命周期(Lifecycle)中的回调(Callbacks) 4.4.
合法性检查(Validatable) 4.5.
XDoclet示例
5.
O/R Mapping基础
-
-
-
5.1.
映射声明(Mapping declaration)
-
-
5.1.1.
Doctype 5.1.2.
hibernate-mapping 5.1.3.
class 5.1.4.
id
-
-
5.1.4.1.
generator 5.1.4.2.
高/低位算法(Hi/Lo Algorithm) 5.1.4.3.
UUID算法(UUID Algorithm ) 5.1.4.4.
标识字段和序列(Identity Columns and Sequences) 5.1.4.5.
程序分配的标识符(Assigned Identifiers)
5.1.5.
composite-id 联合ID 5.1.6.
识别器(discriminator) 5.1.7.
版本(version)(可选) 5.1.8.
时间戳(timestamp )(可选) 5.1.9.
property 5.1.10.
多对一(many-to-one) 5.1.11.
one-to-one 5.1.12.
组件(component), 动态组件(dynamic-component) 5.1.13.
子类(subclass) 5.1.14.
连接的子类(joined-subclass) 5.1.15.
map, set, list, bag 5.1.16.
引用(import)
5.2.
Hibernate 的类型
-
-
-
5.2.1.
实体(Entities)和值(values) 5.2.2.
基本值类型 5.2.3.
持久化枚举(Persistent enum)类型 5.2.4.
自定义值类型 5.2.5.
映射到"任意"(any)类型
5.3.
SQL中引号包围的标识符 5.4.
自定义DDL 5.5.
映射文件的模块化(Modular mapping files)
6.
集合类(Collections)
-
-
-
6.1.
持久化集合类(Persistent Collections) 6.2.
映射集合(Mapping a Collection) 6.3.
值集合和多对多关联(Collections of Values and Many To Many Associations) 6.4.
一对多关联(One To Many Associations) 6.5.
延迟初始化(延迟加载)(Lazy Initialization) 6.6.
集合排序(Sorted Collections) 6.7.
对collection排序的其他方法(Other Ways To Sort a Collection) 6.8.
垃圾收集(Garbage Collection) 6.9.
双向关联(Bidirectional Associations) 6.10.
三重关联(Ternary Associations) 6.11.
异类关联(Heterogeneous Associations) 6.12.
集合例子(Collection Example) 6.13.
<idbag>
7.
组件(Components)
- 8. 操作持久化数据(Manipulating Persistent Data)
-
-
8.1.
创建一个持久化对象 8.2.
装载对象 8.3.
Querying
-
-
8.3.1.
标量查询(Scalar query) 8.3.2.
查询接口(Query interface) 8.3.3.
可滚动迭代(Scrollable iteration) 8.3.4.
过滤集合类(Filtering collections) 8.3.5.
条件查询 8.3.6.
使用本地SQL的查询
8.4.
更改在当前session中保存或者装载的对象 8.5.
更改在以前session中保存或者装载的对象 8.6.
把在先前的session中保存或装载的对象重新与新session建立关联(reassociate) 8.7.
删除持久化对象 8.8.
对象图(Graphs of objects)
- 8.9. 清洗(Flushing) -- 这个词很难翻译,不能使用“刷新”,因为刷新一词已经被"refresh"使用了。有什么好的建议? 8.10. 结束一个Session
-
-
8.10.1.
清洗(Flush)session 8.10.2.
提交事务 8.10.3.
关闭session 8.10.4.
处理异常
8.11.
拦截器(Interceptors) 8.12.
元数据(Metadata) API
9.
父子关系(Parent Child Relationships)
-
-
-
9.1.
关于collections 9.2.
双向的一对多关系(Bidirectional one to many) 9.3.
级联(Cascades) 9.4.
级联更新(Using cascading update()) 9.5.
结论
10.
Hibernate查询语言(Query Language), 即HQL
-
-
10.1.
大小写敏感性(Case Sensitivity) 10.2.
from 子句 10.3.
联合(Associations)和连接(joins) 10.4.
select子句 10.5.
统计函数(Aggregate functions) 10.6.
多形(polymorphism) 10.7.
where子句 10.8.
表达式(Expressions) 10.9.
order by 子句 10.10.
group by 子句 10.11.
子查询 10.12.
示例 10.13.
提示和技巧(Tips & Tricks)
11.
实例(A Worked Example)
-
-
11.1.
持久化类 11.2.
Hibernate 映射 11.3.
Hibernate Code
12.
性能提升(Improving Performance)
-
-
12.1.
用于延迟装载的代理 12.2.
第二层缓存(The Second Level Cache)s
-
-
12.2.1.
映射(Mapping) 12.2.2.
只读缓存 12.2.3.
读/写缓存 12.2.4.
Nonstrict Read / Write Cache 12.2.5.
事务缓存(transactional)
12.3.
管理Session缓存 12.4.
查询缓存(Query Cache)
13.
理解集合类的性能(Understanding Collection Performance)
-
-
-
13.1.
分类(Taxonomy) 13.2.
Lists, maps 和sets用于更新效率最高 13.3.
Bag和list是反向集合类中效率最高的 13.4.
一次性删除(One shot delete)
14.
条件查询(Criteria Query)
-
-
14.1.
创建一个Criteria实例 14.2.
缩小结果集范围 14.3.
对结果排序 14.4.
关联(Associations) 14.5.
动态关联对象获取(Dynamic association fetching) 14.6.
根据示例查询(Example queries)
15.
SQL查询
-
-
15.1.
Creating a SQL based Query 15.2.
Alias and property references 15.3.
为SQL查询命名
16.
继承映射(Inheritance Mappings)
- 17. 事务和并行(Transactions And Concurrency)
-
-
17.1.
配置,会话和工厂(Configurations, Sessions and Factories) 17.2.
线程和连接(Threads and connections) 17.3.
乐观锁定/版本化(Optimistic Locking / Versioning)
-
-
17.3.1.
使用长生命周期带有自动版本化的会话 17.3.2.
使用带有自动版本化的多个会话 17.3.3.
应用程序自己进行版本检查
17.4.
会话断开连接(Session disconnection) 17.5.
悲观锁定(Pessimistic Locking)
18.
映射实例(Mapping Examples)
-
-
-
18.1.
雇员/雇主(Employer/Employee) 18.2.
作者/著作(Author/Work) 18.3.
客户/订单/产品(Customer/Order/Product)
19.
工具箱指南
-
-
19.1.
Schema 生成器(Schema Generation)
-
-
19.1.1.
对schema定制化(Customizing the schema) 19.1.2.
运行该工具 19.1.3.
属性(Properties) 19.1.4.
使用Ant(Using Ant) 19.1.5.
对schema的增量更新(Incremental schema updates) 19.1.6.
用Ant来增量更新schema(Using Ant for incremental schema updates)
19.2.
代码生成(Code Generation)
-
-
19.2.1.
配置文件(可选) 19.2.2.
meta属性 19.2.3.
基本的finder生成器(Basic finder generator) 19.2.4.
基于Velocity的渲染器/生成器(Velocity based renderer/generator)
19.3.
映射文件生成器(Mapping File Generation)
-
-
19.3.1.
运行此工具
20.
最佳实践(Best Practices)
-
在今日的企业环境中,把面向对象的软件和关系数据库一起使用可能是相当麻烦、浪费时间的。Hibernate是一个面向Java环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL的关系模型结构中去。
Hibernate不仅仅管理Java类到数据库表的映射,还提供数据查询和获取数据的方法,可以大幅度减少开发时人工使用SQL和JDBC处理数据的时间。Hibernate的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。
如果你对Hibernate和对象/关系数据库映射还是个新手,或者甚至对Java也不熟悉,请按照下面的步骤来学习。
阅读这个30分钟就可以结束的Chapter 1, 在Tomcat中快速上手,它使用Tomcat。
阅读Chapter 2, 体系结构来理解Hibernate可以使用的环境。
查看Hibernate发行包中的eg/目录,里面有一个简单的独立运行的程序。把你的JDBC驱动拷贝到lib/目录下,修改一下src/hibernate.properties,指定其中你的数据库的信息。进入命令行,切换到你的发行包的目录,输入ant eg(使用了Ant),或者在Windows操作系统中使用build eg。
把这份参考文档作为你学习的主要信息来源。
在Hibernate 的网站上可以找到经常提问的问题与解答(FAQ)。
在Hibernate网站上还有第三方的演示、示例和教程的链接。
Hibernate网站的“社区(Community Area)”是讨论关于设计模式以及很多整合方案(Tomcat, JBoss, Spring,Struts, EJB,等等)的好地方。
离线版本的Hibernate网站随着Hibernate发行包一起发布,位于doc/目录下。
如果你有问题,请使用Hibernate网站上链接的用户论坛。我们也提供一个JIRA问题追踪系统,来搜集bug报告和新功能请求。如果你对开发Hibernate有兴趣,请加入开发者的邮件列表。 (译者注:目前Hibernate已经有一个中文的用户论坛,URL是http://forum.hibernate.org.cn 我们随时欢迎您的访问。)
翻译说明
=========================================================
本文档的翻译是在网络上协作进行的,也会不断根据Hibernate的升级进行更新。提供此文档的目的是为了减少学习Hibernate的坡度,而非代替原文档。我们建议所有有能力的读者都直接阅读英文原文。
若您对翻译有异议,或发现翻译错误,敬请不吝赐教,请到Hibernate中文论坛(http://forum.hibernate.org.cn)提出,或报告到如下email地址:caoxg at redsaga.com
第6章(集合类)、第7章(组件)是由jlinux翻译,第10章(父子关系)是由muziq翻译,第16章(事务和并行)、第17章(映射实例)是由liangchen翻译,其他各章节是由曹晓钢翻译的,第18、19、20章,bruce、robbin也有贡献。曹晓钢也进行了全书从2.0.4更新到2.1.1版本的工作。
更详细的翻译者与翻译更新情况,请查阅CVS目录下的TRANSLATE-LOG.TXT文件。
版权声明
=========================================================
Hibernate英文文档属于Hibernate发行包的一部分,遵循LGPL协议。本翻译版本同样遵循LGPL协议。参与翻译的译者一致同意放弃除署名权外对本翻译版本的其它权利要求。
您可以自由链接、下载、传播此文档,或者放置在您的网站上,甚至作为产品的一部分发行。但前提是必须保证全文完整转载,包括完整的版权信息和作译者声明。这里“完整”的含义是,不能进行任何删除/增添/注解。若有删除/增添/注解,必须明确声明那些部分并非本文档的一部分。
这份教程讨论如何在Apache Tomcat servlet容器中为web程序安装Hibernate 2.1。Hibernate在大多数主流J2EE引用服务器 的受管理环境中都可以良好运作,也可以作为独立应用程序运行。在本例中的示例数据库系统是PostgreSQL 7.3,当然也可以 很容易的换成Hibernate 支持的其它16种数据库之一。
第一步是拷贝所有需要的运行库到Tomcat去。在这篇教程中,我们使用一个单独的web程序(webapps/quickstart)。我们要考虑全局库文件搜索路径(TOMCAT/common/lib)和本web应用程序上下文的类装载器搜索路径(对于jar来说是webapps/quickstart/WEB-INF/lib,对于class文件来说是webapps/quickstart/WEB-INF/classes)。我们把这两个类装载器级别分别称为全局类路径(global classpath)和上下文类路径(context classpath)。
首先,把数据库需要的JDBC驱动拷贝到全局类路径。这是tomcat附带的DBCP连接池软件所要求的。对于本教程来说,把pg73jdbc3.jar库文件(对应PostgreSQL 7.3和JDK 1.4)到全局类装载器路径去。如果你使用一个不同的数据库,拷贝相应的JDBC 驱动)。
不要拷贝任何其他东西到全局类装载器去。否则你可能在一些工具上遇到麻烦,比如log4j, commons-logging等。 记得要使用每个web应用程序自己的上下文类路径,就是说把你自己的类库拷贝到WEB-INF/lib下去,把配置文件configuration/property拷贝到WEB-INF/classes下面去。这两个目录默认都是上下文类路径级别的。
Hibernate本身打包成一个JAR库。hibernate2.jar文件要和你应用程序的其他库文件一起放在上下文类路径中。在运行时,Hibernate还需要一些第三方库,它们在Hibernate发行包的lib/目录下。参见Table 1.1。把你需要的第三方库文件也拷贝到上下文类路径去。
要为Tomcat和Hibernate都配置数据库连接。也就是说Tomcat要负责提供JDBC连接池,Hibernate通过JNDI来请求这些连接。Tomcat把连接池绑定到JNDI。
Table 1.1. Hibernate 第三方库
库 | 描述 |
---|---|
dom4j (必需) | Hibernate在解析XML配置和XML映射元文件时需要使用dom4j。 |
CGLIB (必需) | Hibernate在运行时使用这个代码生成库强化类(与Java反射机制联合使用)。 |
Commons Beanutils, Commons Collections, Commons Lang, Commons Logging (必需) | Hibernat使用Apache Jakarta Commons项目提供的多个工具类库。 |
ODMG4 (必需) | Hibernate提供了一个可选的ODMG兼容持久化管理界面。如果你需要映射集合,你就需要这个类库,就算你不是为了使用ODMG API。我们在这个教程中没有使用集合映射,但不管怎样把这个JAR拷贝过去总是不错的。 |
Log4j (可选) | Hibernate使用Commons Logging API,后者可以使用Log4j作为实施log的机制。如果把Log4j库放到上下文类目录中,Commons Logging酒会使用Log4j和它在上下文类路径中找到的log4j.properties文件。在Hibernate发行包中包含有一个示例的properties文件。所以,也把log4j.jar拷贝到你的上下文类路径去吧。 |
其他文件是不是必需的? | 请察看Hibernate发行包中的/lib/README.txt文件。这是一个Hibernate发行包中附带的第三方类库的列表,总是保持更新。你可以在那里找到所有必需或者可选的类库的列表。 |
好了,现在所有的类库已经被拷贝过去了,让我们在Tomcat的主配置文件,TOMCAT/conf/server.xml中增加一个数据库JDBC连接池的资源声明,
<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目录。要访问任何Servlet,在你的浏览器中访问http://localhost:8080/quickstart就可以了。
Tomcat在这个配置下,使用DBCP连接池,通过JNDI位置:java:comp/env/jdbc/quickstart提供带有缓冲池的JDBCConnections。如果你在让连接池工作的时候遇到困难,请查阅Tomcat文档。如果你得到了JDBC驱动的exception信息,请先不要用Hibernate,测试JDBC连接池本身是否正确。Tomcat和JDBC的教程可以在Web上查到。
下一步是配置hibernate,来使用绑定到JNDI的连接池中提供的连接。我们使用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-2.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">net.sf.hibernate.dialect.PostgreSQLDialect</property> <!-- Mapping files --> <mapping resource="Cat.hbm.xml"/> </session-factory> </hibernate-configuration>
我们关闭了SQL命令的log,告诉Hibernate使用哪种SQL数据库方言(dialet),还有如何得到JDBC连接(通过声明数据源池绑定的JNDI地址)。方言是必需的,因为不同的数据库都和SQL "标准"有一些出入。Hibernate会替你照管这些差异之处,发行包包含了所有主流的商业和开放源代码数据库的方言。
SessionFactory是Hibernate的概念,对应一个数据存储源,如果有多个数据库,可以创建多个XML配置文件,也在你的程序中创建多个Configuration和SessionFactory对象。
在hibernate.cfg.xml中的最后一个元素声明了Cat.hbm.xml是一个Hibernate XML映射文件,对应持久化类Cat。这个文件包含了把POJO类映射到数据库表(或多个数据库表)的元数据。我们稍后就回来看这个文件。让我们先编写这个POJO类,再在声明它的映射元数据。
s Hibernate让普通的Java对象(Plain Old Java Objects ,就是POJOs,有时候也称作Plain Ordinary Java Objects)变成持久化类。一个POJO很像JavaBean,属性通过getter和setter方法访问,对外隐藏了内部实现的细节。
package net.sf.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; } public 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和float)都可以被映射,也包括Java 集合框架(Java collections framework)中的类。你可以把它们映射成为值,值集合,或者与其他实体相关联。id是一个特殊的属性,代表了这个类的数据库标识符(主键),它对于类似于Cat这样的实体是必需的。
持久化类不需要实现什么特别的接口,也不需要从一个特别的持久化根类继承下来。Hibernate也不需要使用任何编译期处理,比如字节码增强操作,它独立的使用Java反射机制和运行时类增强(通过CGLIB)。所以,在Hibernate中,POJO的类不需要任何前提条件,我们就可以把它映射成为数据库表。
Cat.hbm.xml映射文件包含了对象/关系映射所需的元数据。
原数据包含了持久化类的声明和把它与其属性映射到数据库表的信息(属性作为值或者是指向其他实体的关联)。
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"> <hibernate-mapping> <class name="net.sf.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" sql-type="varchar(16)" not-null="true"/> </property> <property name="sex"/> <property name="weight"/> </class> </hibernate-mapping>
每个持久化类都需要一个标识属性(实际上,只是哪些代表一手对象的类,而不是代表值对象的类,后者会被映射称为一手对象中的一个组件)。这个属性用来区分持久化对象:如果catA.getId().equals(catB.getId())结果是true的话,两只猫就是相同的。这个概念称为数据库标识。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)
你现在可以在你的数据库中首先创建这个表了,如果你需要使用SchemaExport工具把这个步骤自动化,请参阅Chapter 19, 工具箱指南。这个工具能够创建完整的SQL DDL,包括表定义,自定义的字段类型约束,惟一约束和索引。
我们现在可以开始Hibernate的Session了。我们用它来从数据库中存取Cat。首先,我们要从SessionFactory中获取一个Session(Hibernate的工作单元)。
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
SessionFactory负责一个数据库,也只对应一个XML配置文件(hibernate.cfg.xml)。
这篇教程的关注点在于配置Tomcat的JDBC连接,绑定到JNDI上,以及Hibernate的基础配置。你可以用喜欢的任何方式编写一个Servlet,包含下面的代码,只要确保SessionFactory只创建一次。也就是说你不能把它作为你的Serlvet 的实例变量。一个好办法是用在辅助类中用一个静态的SessionFactory,例如这样:
import net.sf.hibernate.*; import net.sf.hibernate.cfg.*; public class HibernateUtil { private static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (HibernateException ex) { throw new RuntimeException("Exception building SessionFactory: " + ex.getMessage(), ex); } } public static final ThreadLocal session = new ThreadLocal(); public static Session currentSession() throws HibernateException { 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() throws HibernateException { Session s = (Session) session.get(); session.set(null); if (s != null) s.close(); } }
这个类不但在它的静态属性中使用了SessionFactory,还使用了ThreadLocal来为当前工作线程保存Session。
Session不是线程安全的,代表与数据库之间的一次操作。Session通过SessionFactory打开,在所有的工作完成后,需要关闭:
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。你必须确保Session在你的数据库事务完成后关闭,不管是在你的Servlet代码中,或者在ServletFilter中,HTTP结果返回之前。
Hibernate有不同的方法来从数据库中取回对象。最灵活的方式是使用Hibernate查询语言(HQL),这是一种容易学习的语言,是对SQL的面向对象的强大扩展。
Transaction tx= session.beginTransaction(); Query query = session.createQuery("select cat from Cat as cat where cat.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非常高层的概览:
![](http://www.huihoo.com/java/hibernate/hibernate-ref/2.1.1/images/overview.gif)
这幅图展示了Hibernate使用数据库和配置文件数据来为应用程序提供持久化服务(和持久化的对象)。
让我们更细致地观察一下运行时的体系结构。 挺不幸的,Hibernate是比较复杂的,提供了好几种不同的运行方式。我们展示一下两种极端情况。轻型体系中,应用程序自己提供JDBC连接,并且自行管理事务。这种方式使用了Hibernate API的一个最小子集。
![](http://www.huihoo.com/java/hibernate/hibernate-ref/2.1.1/images/lite.gif)
全面解决体系中,对于应用程序来说,所有的底层JDBC/JTA API都被抽象了,Hibernate会替你照管所有的细节。
![](http://www.huihoo.com/java/hibernate/hibernate-ref/2.1.1/images/full_cream.gif)
下面是图中一些对象的定义:
-
SessionFactory (net.sf.hibernate.SessionFactory)
-
对编译过的映射文件的一个线程安全的,不可变的缓存快照。它是Session的工厂。是ConnectionProvider的客户。
可能持有事务之间重用的数据的缓存。
会话,Session (net.sf.hibernate.Session)
-
单线程,生命期短促的对象,代表应用程序和持久化层之间的一次对话。封装了一个JDBC连接。也是Transaction的工厂。
持有持久化对象的缓存。
持久化对象(Persistent Object)及其集合(Collection)
-
生命期短促的单线程的对象,包含了持久化状态和商业功能。它们可能是普通的JavaBeans,唯一特别的是他们现在从属于且仅从属于一个Session。
临时对象(Transient Object)及其集合(Collection)
-
目前没有从属于一个Session的持久化类的实例。他们可能是刚刚被程序实例化,还没有来得及被持久化,或者是被一个已经关闭的Session所实例化的。
事务,Transaction (net.sf.hibernate.Transaction)
-
(可选) 单线程,生命期短促的对象,应用程序用它来表示一批工作的原子操作。是底层的JDBC,JTA或者CORBA事务的抽象。一个Session可能跨越多个Transaction 事务。
ConnectionProvider (net.sf.hibernate.connection.ConnectionProvider)
-
(可选)JDBC连接的工厂和池。从底层的Datasource或者 DriverManager抽象而来。对应用程序不可见。
TransactionFactory (net.sf.hibernate.TransactionFactory)
-
(可选)事务实例的工厂。对应用程序不可见。
在上面的轻型结构中,程序没有使用Transaction / TransactionFactory 或者ConnectionProvider API,直接和JTA/JDBC对话了。
应用程序可能同时在两个不同的session中存取同一个持久化对象。然而,两个Session实例是不可能共享一个持久化类的实例的。有两种不同的用来辨别对象是否相同的方法。
-
Persistent Identity,持久化辨别
-
foo.getId().equals( bar.getId() )
JVM Identity, JVM辨别
-
foo==bar
对于同一个特定的Session返回的对象来说,这二者是等价的。然而,当程序并行在两个不同的session中访问含义上“相同”(持久化辨别)的商业对象时,两个对象实例从JVM的角度上来看却是“不同”的(JVM辨别)
这种方式把并行访问(应用程序不需要对任何商业对象进行同步,只要求遵循每个Session一个线程的原则)和对象辨别(在应用程序的一个session之中,可以安全的用==来比较对象)的难题留给了Hibernate和数据库。
JMX是用来管理Java组件的J2EE标准。Hibernate可以被标准的JMX Mbean管理,但是因为大多数程序还没有支持JMX,Hibernate也支持一些非标准的配置方式。
请查阅Hibernate网站,可以得到关于如何在JBOSS中把Hibernate配置成为一个JMX组件的更多信息。
因为Hibernate被设计为可以在许多不同环境下工作,所以它有很多配置参数。幸运的是,大部分都已经有默认值了,Hibernate发行包中还附带有示例的hibernate.properties文件,它演示了一些可变的参数。
net.sf.hibernate.cfg.Configuration的一个实例代表了应用程序中所有的Java类到关系数据库的映射的集合。这些映射是从一些XML映射文件中编译得来的。你可以得到一个Configuration的实例,直接实例化它即可。下面有一个例子,用来从两个XML配置文件中的映射中初始化:
Configuration cfg = new Configuration() .addFile("Vertex.hbm.xml") .addFile("Edge.hbm.xml");
另外一个(或许是更好的)方法是让Hibernate自行用getResourceAsStream()来装载映射文件。
Configuration cfg = new Configuration() .addClass(eg.Vertex.class) .addClass(eg.Edge.class);
Hibernate 就会在classpath中寻找叫做/eg/Vertex.hbm.xml、/eg/Edge.hbm.xml的映射文件。这种方法取消了所有对文件名的硬编码。
Configuration也可以指定一些可选的配置项。
Properties props = new Properties(); ... Configuration cfg = new Configuration() .addClass(eg.Vertex.class) .addClass(eg.Edge.class) .setProperties(props);
Configuration是仅在配置期使用的对象,从第一个SessionFactory开始建立的时候,它就失效了。
当所有的映射都被Configuration解析之后,应用程序为了得到Session实例,必须先得到它的工厂。这个工厂应该是被应用程序的所有线程共享的。当然,Hibernate并不禁止你的程序实例化多个SessionFactory。在你使用不止一个数据库的时候,这就有用了。
SessionFactory sessions = cfg.buildSessionFactory();
SessionFactory可以使用一个用户自行提供的JDBC连接来打开一个Session。这种设计可以让应用程序来自己管理JDBC连接。应用程序必须小心,不能在同一个连接上打开多个并行的session。
java.sql.Connection conn = datasource.getConnection(); Session sess = sessions.openSession(conn); // start a new transaction (optional) Transaction tx = sess.beginTransaction();
上面的最后一行是可选的——应用程序也可能选择自行管理JTA或者JDBC事务。当然,假若你使用Hibernate Transaction,你的客户代码就可以从底层的实现中抽象出来了。(比如说,你可以将来在需要的时候切换到CORBA连接,而不需要更改程序代码。)
另一种方法就是,你可以让SessionFactory替你打开连接。SessionFactory必须事先知道连接的参数,有几种不同的方法设置参数:
传递一个java.util.Properties到Configuration.setProperties()方法。
在classpath的根目录中提供hibernate.properties文件。
通过java -Dproperty=value指定使用系统属性。
在hibernate.cfg.xml文件中包含<property>元素。详情见后。
如果你使用这种方法,打开一个Session是非常简单的:
Session sess = sessions.openSession(); // obtain a JDBC connection and // instantiate a new Session // start a new transaction (optional) Transaction tx = sess.beginTransaction();
所有的Hibernate属性名和约束都在net.sf.hibernate.cfg.Environment类中定义。我们讨论一下最重要的几项设置:
假若你设置了如下的属性,Hibernate会使用java.sql.DriverManager来得到连接,并建立连接池:
Table 3.1. Hibernate JDBC属性
属性名 | 用途 |
---|---|
hibernate.connection.driver_class | jdbc驱动类 |
hibernate.connection.url | jdbc URL |
hibernate.connection.username | 数据库用户名 |
hibernate.connection.password | 数据库用户密码 |
hibernate.connection.pool_size | 连接池容量最大数 |
Hibernate的连接池算法是非常可配置的。它的用途是让你上手,但是并非让你在生产系统中使用的,甚至不是用来做性能测试的。
C3P0是随Hibernate发行包一起发布的一个开放源代码JDBC连接池,你可以在lib 目录中找到。假若你设置了hibernate.c3p0.* 属性,Hibernate会使用内置的C3P0ConnectionProvider作为连接池。 对Apache DBCP和Proxool的支持也是内置的。你必须设置hibernate.dbcp.*属性 (DBCP连接池属性)和hibernate.dbcp.ps.* (DBCP 语句缓存属性)才能使用DBCPConnectionProvider。要知道它们的含义,请查阅Apache commons-pool的文档。如果你想要用Proxool,你需要设置hibernate.proxool.*系列属性。
在Application Server内使用时,Hibernate可以从JNDI中注册的javax.sql.Datasource取得连接。需要设置如下属性:
下面是一些在运行时可以改变Hibernate行为的其他配置。所有这些都是可选的,也有合理的默认值。
系统级别的配置只能通过java -Dproperty=value或者在hibernate.properties文件中配置,而不能通过传递给Configuration的Properties实例来配置。
Table 3.3. Hibernate配置属性
属性名 | 用途 |
---|---|
hibernate.dialect | Hibernate方言(Dialect)的类名 - 可以让Hibernate使用某些特定的数据库平台的特性 取值. full.classname.of.Dialect |
hibernate.default_schema | 在生成的SQL中,scheml/tablespace的全限定名 取值. SCHEMA_NAME |
hibernate.session_factory_name | 把SessionFactory绑定到JNDI中去. 取值. jndi/composite/name |
hibernate.use_outer_join | 允许使用外连接抓取. 取值. true | false |
hibernate.max_fetch_depth | 设置外连接抓取树的最大深度 取值. 建议设置为0到3之间 |
hibernate.jdbc.fetch_size | 一个非零值,用来决定JDBC的获取量大小。(会调用calls Statement.setFetchSize()). |
hibernate.jdbc.batch_size | 一个非零值,会开启Hibernate使用JDBC2的批量更新功能 取值. 建议值在 5 和 30之间。 |
hibernate.jdbc.use_scrollable_resultset | 允许Hibernate使用JDBC2提供的可滚动结果集。只有在使用用户自行提供的连接时,这个参数才是必需的。否则Hibernate会使用连接的元数据(metadata)。 取值. true | false |
hibernate.jdbc.use_streams_for_binary | 在从JDBC读写binary(二进制)或者serializable(可序列化)类型时,是否使用stream(流). 这是一个系统级别的属性。 取值. true | false |
hibernate.cglib.use_reflection_optimizer | 是否使用CGLIB来代替运行时反射操作。(系统级别属性,默认为在可能时都使用CGLIB).在调试的时候有时候使用反射会有用。 取值. true | false |
hibernate.jndi.<propertyName> | 把propertyName这个属性传递到JNDI InitialContextFactory去 (可选) |
hibernate.connection.isolation | 事务隔离级别 (可选) 取值. 1, 2, 4, 8 |
hibernate.connection.<propertyName> | 把 propertyName这个JDBC 属性传递到DriverManager.getConnection()去. |
hibernate.connection.provider_class | 指定一个自定义的ConnectionProvider类名 取值. classname.of.ConnectionProvider |
hibernate.cache.provider_class | 指定一个自定义的CacheProvider缓存提供者的类名 取值. classname.of.CacheProvider |
hibernate.transaction.factory_class | 指定一个自定义的TransactionFactory类名,Hibernate Transaction API将会使用. 取值. classname.of.TransactionFactory |
jta.UserTransaction | JTATransactionFactory 用来获取JTA UserTransaction的JNDI名. 取值. jndi/composite/name |
hibernate.transaction.manager_lookup_class | TransactionManagerLookup的类名 - 当在JTA环境中,JVM级别的缓存被打开的时候使用. 取值. classname.of.TransactionManagerLookup |
hibernate.query.substitutions | 把Hibernate查询中的一些短语映射为SQL短语。(比如说短语可能是函数或者字符) . 取值. hqlLiteral=SQL_LITERAL, hqlFunction=SQLFUNC |
hibernate.show_sql | 把所有的SQL语句都输出到控制台(可以作为log功能的一个替代). 取值. true | false |
hibernate.hbm2ddl.auto | 自动输出schema创建DDL语句. 取值. update | create | create-drop |
你总是可以为你的数据库设置一个hibernate.dialect方言,它是net.sf.hibernate.dialect.Dialect 的一个子类。如果你不需要使用基于native或者sequence的主键自动生成算法,或者悲观锁定(使用Session.lock() 或 Query.setLockMode())的话,方言就可以不必指定。然而,假若你指定了一个方言,Hibernate会为上面列出的一些使用特殊默认值,省得你手工指定它们。
Table 3.4. Hibernate SQL 方言 (hibernate.dialect)
RDBMS | 方言 |
---|---|
DB2 | net.sf.hibernate.dialect.DB2Dialect |
MySQL | net.sf.hibernate.dialect.MySQLDialect |
SAP DB | net.sf.hibernate.dialect.SAPDBDialect |
Oracle (所有版本) | net.sf.hibernate.dialect.OracleDialect |
Oracle 9 | net.sf.hibernate.dialect.Oracle9Dialect |
Sybase | net.sf.hibernate.dialect.SybaseDialect |
Sybase Anywhere | net.sf.hibernate.dialect.SybaseAnywhereDialect |
Progress | net.sf.hibernate.dialect.ProgressDialect |
Mckoi SQL | net.sf.hibernate.dialect.MckoiDialect |
Interbase | net.sf.hibernate.dialect.InterbaseDialect |
Pointbase | net.sf.hibernate.dialect.PointbaseDialect |
PostgreSQL | net.sf.hibernate.dialect.PostgreSQLDialect |
HypersonicSQL | net.sf.hibernate.dialect.HSQLDialect |
Microsoft SQL Server | net.sf.hibernate.dialect.SybaseDialect |
Ingres | net.sf.hibernate.dialect.IngresDialect |
Informix | net.sf.hibernate.dialect.InformixDialect |
FrontBase | net.sf.hibernate.dialect.FrontbaseDialect |
如果你的数据库支持ANSI或者Oracle风格的外连接,外连接抓取可能提高性能,因为可以限制和数据库交互的数量(代价是数据库自身进行了更多的工作)。外连接抓取允许你在一个select语句中就可以得到一个由多对一或者一对一连接构成的对象图。
默认情况下,抓取在叶对象,拥有代理的对象或者产生对自身的引用时终止。对一个特定关联来说,通过在XML映射文件中设置outer-join属性可以控制是否开启抓取功能。也可以设置hibernate.use_outer_join为false来全局关闭此功能。 你也可以通过hibernate.max_fetch_depth来设置抓取得对象图的最大深度。
Oracle限制通过它的JDBC驱动传递的byte数组的大小。如果你希望使用很大数量的binary或者serializable 类型的话,你需要打开hibernate.jdbc.use_streams_for_binary。这只能通过JVM级别设定
你可以自定义你的获取JDBC连接的策略,只需要实现net.sf.hibernate.connection.ConnectionProvider接口。在hibernate.connection.provider_class设置你自己的实现的类名。
几个配置属性影响除了DatasourceConnectionProvider之外的所有内置连接提供者.它们是: hibernate.connection.driver_class, hibernate.connection.url, hibernate.connection.username and hibernate.connection.password.
hibernate.connection.isolation应该指定为一个整数值。(查阅java.sql.Connection可以得到值的含义,但注意大多数数据库不会支持所有的隔离级别。)
专用的连接属性可以通过在"hibernate.connnection"后面加上属性名来指定。比如,你可以通过hibernate.connnection.charSet指定一个charSet。
通过实现net.sf.hibernate.connection.ConnectionProvider接口,你可以整合一个JVM级别(或者集群的)缓存进来。你可以通过hibernate.cache.provider_class选择某个子定义的实现。
如果你希望使用Hibernate的Transaction API,你必须通过hibernate.transaction.factory_class属性指定一个Transaction实例的工厂类。 内置的两个标准选择是:
-
net.sf.hibernate.transaction.JDBCTransactionFactory
-
使用数据库(JDBC)事务
net.sf.hibernate.transaction.JTATransactionFactory
-
使用JTA(假若已经存在一个事务,Session会在这个上下文中工作,否则会启动一个新的事务。)
你也可以自行定义你的事务策略(比如说,一个CORBA事务服务)。
如果你希望在JTA环境中为可变数据使用JVM级别的缓存,你必须指定一个获取JTA TransactionManager的策略。
Table 3.5. JTA TransactionManagers
事务工厂类 | Application Server |
---|---|
net.sf.hibernate.transaction.JBossTransactionManagerLookup | JBoss |
net.sf.hibernate.transaction.WeblogicTransactionManagerLookup | Weblogic |
net.sf.hibernate.transaction.WebSphereTransactionManagerLookup | WebSphere |
net.sf.hibernate.transaction.OrionTransactionManagerLookup | Orion |
net.sf.hibernate.transaction.ResinTransactionManagerLookup | Resin |
net.sf.hibernate.transaction.JOTMTransactionManagerLookup | JOTM |
net.sf.hibernate.transaction.JOnASTransactionManagerLookup | JOnAS |
net.sf.hibernate.transaction.JRun4TransactionManagerLookup | JRun4 |