本文摘自 李刚 著 《Java EE企业级应用实战》
Hibernate 是一种面向Java环境对象/关系数据库映射工具,用来把对象模型表示的对象映射到基于SQL的关系模型数据结构中去。 Hibernate的目标是解放开发者通常的数据持久化相关编程任务的95%。对于以数据为核心的程序而言,往往在数据库中使用存储过程来实现商业逻辑,Hibernate是最有用的。不管怎样,Hibernate能够消除那些针对特定数据库厂商的SQL代码,并且结果集从表格式的形式转换成值对象的形式。
Hibernate不仅仅管理了Java类到数据库表的映射(包括Java 数据类型到SQL数据类型的映射),还提供查询数据和获取数据的方法,可以大幅度地减少开发时人工使用SQL和JDBC处理数据的时间。
HIbernate能够从众多的ORM框架中脱颖而出,因为Hibernate和其他的框架对比有以下的优势:
(1)开源免费的License,方便需要时研究源代码,改写源代码,进行功能定制
(2)轻量级封装,避免引入过多复杂的问题,调试容易,减轻程序员的负担。
(3)具有可扩展性,API开放。功能不够用时,自己进行编码扩展。
(4)开发者活跃,有稳定的发展保障
一. Hibernate解压包中的文件结构
(1)doc:该路径下存放了Hibernate 的相关文档,包括Hibernate的参考文档和相关API 文档。
(2)eg:该文件夹下存放了一个示例应用的持久化类和映射文件通常没有太大的用处。
(3)etc:该文件夹下存放了HIbernate的各种配置文件的范例,有很大的参考意义。
(4)lib:该路径下存放了Hibernate编译和运行所依赖的第三方类库。
(5)src:该路径下存放了Hibernate的所有源文件。
(6)test:该路径下存放了Hibernate的各种功能的测试程序,有极好的参考价值。
(7)Hibernate3.jar:该文件是Hibernate 的核心类库。
(8)生成文件、授权文件和杂项文件通常没有什么用处。
安装Hibernate时,将Hibernate3.jar文件复制到需要Hibernate 的应用中,如果需要第三方类库则还需要复制第三方类库。如果是web应用,则将上述的文件复制到WEB-INF/lib路径下。
如果要在控制台直接编译使用了Hibernate API的类,则需要将Hibernate 的核心库文件Hibernate3.jar添加到CLASSPATH里,而如果使用的是Ant, 或者Eclipse等IDE工具则无需修改环境变量。
二. Hibernate的数据库操作
在所有的ORM框架中有一个非常重要的媒介:PO(Persistent Object 持久化对象)。持久化对象的作用是完成持久化操作,简单地说,就是就是通过该对象可以对数据进行增、删、改的操作——以面向对象的方式操作数据库。
应用程序无需直接访问数据库,甚至无需理会底层的数据库采用何种数据库——这一切对于应用程序完全透明,应用程序只需创建、删除、修改持久化对象即可:与此同时,Hibernate 则负责将这些操作转换为对指定数据库的操作。
Hibernate里面的PO是非常简单的。我们知道Hibernate是低侵入式设计的,完全采用普通的Java 对象来作为持久化对象来使用,请看下面的POJO类:
package ppp;
public class News {
// 消息类的标识属性
private int id;
// 消息标题
private String title;
// 消息内容
private String content;
// 构造器
public News() { }
// 标识属性的setter和getter方法
public int getId() {
return (this.id);
}
public void setId(int id) {
this.id = id;
}
//消息标题的setter和getter方法
public String getTitle() {
return (this.title);
}
public void setTitle(String title) {
this.title = title;
}
//消息内容setter和getter方法
public String getContent() {
return (this.content);
}
public void setContent(String content) {
this.content = content;
}
}
仔细看上面的这个类的代码,无法发现这个类与普通的Java Bean有任何的区别。实际上,Hibernate直接采用了POJO(普通、传统Java对象)作为PO,这就是Hibernate被称为低侵入式设计的原因,Hibernate不要求将持久化类继承任何的父类,或者实现任何的借口,这样可以保证代码就不会受到污染。
这个普通的Java Bean目前还不具备持久化操作的能力,为了使其具备持久化操作的能力,Hibernate采用XML 映射文件,该映射文件也是非常简单的。下面提供该XML映射文件的全部代码:
<?xml version = "1.0" encoding = "gb2132"?>
<!-- 指定Hibernate 3 映射文件的DTD信息-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//HIbernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 上面的4行对于所有的Hibernate映射文件都相同-->
<!-- hibernate-mapping是映射文件的根元素-->
<hibernate-mapping package="lee">
<!-- 每个class对应一个持久化对象 -->
<class name="News" table="news-table">
<!-- id元素定义持久化对象的标识属性 -->
<id name="id" unsaved-value="null">
<generator class="identity" />
</id>
<!-- property元素定义常规属性 -->
<property name="title" />
<property name="content" />
</class>
</hibernate-mapping>
对于这个文件需要简单地解释一下:
映射文件的第一行属于XML声明部分,它指定了XML文件的版本,编码所用的字符集信息;映射文件的2、3、4行指定了Hibernate映射文件的DTD信息,这四行对于所有的Hibernate3.2映射文件全部相同。Hibernate-mapping元素是所有的Hibernate映射文件的根元素,这个根元素对于所有的映射文件都是相同的。
Hibernate-mapping元素下有class子元素,每个class子元素映射一个PO,更准确地说是一个持久化类。所以我们可以这样理解:PO = POJO + 映射文件。
现在就可以通过这个持久化类完成对于数据库的访问,即插入一条消息。
通过上面的映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性和数据表列之间的对应关系。但是无法知道连接到的是哪个数据库,以及连接数据库时所用到连接池、用户名和密码等详细信息。这些信息对于所有的持久化类都是通用的,我们把这些通用信息称之为Hibernate配置信息,配置信息使用配置文件指定。
Hibernate配置文件可以使用*.properties的属性文件,也可以使用XML 文件进行配置。在实际应用中通常使用XML文件进行配置。下面是一个示例中的XML配置文件的详细信息:
<?xml version = "1.0" encoding = "gb2132"?>
<!-- 指定Hibernate 3 映射文件的DTD信息-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//HIbernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- hibernate-configuration是连接配置文件的根元素 -->
<hibernate-configuration>
<session-factory>
<!-- 指定连接数据库所用的驱动 -->
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- 指定连接数据库的url,hibernate连接的数据库名 -->
<property name="connection.url">
jdbc:mysql://localhost/hibernate
</property>
<!-- 指定连接数据库的用户 -->
<property name="connection.username">
root
</property>
<!-- 指定连接数据库的密码 -->
<property name="connection.password">
12345
</property>
<!-- 指定连接池中的最大连接数 -->
<property name="hibernate.c3p0.max_size">
20
</property>
<!-- 指定连接池中的最小连接数 -->
<property name="hibernate.c3p0.min_size">
1
</property>
<!-- 指定连接池里连接的超时时长 -->
<property name="hibernate.c3p0.timeout"></property>
<!-- 指定连接池里最大缓存多少个Statement对象 -->
<property name="hibernate.c3p0.max_statements">
100
</property>
<property name="hibernate.c3p0.idle_test_period">
3000
</property>
<property name="hibernate.c3p0.acquire_increment">
2
</property>
<property name="hibernate.c3p0.validate">
true
</property>
<!-- 指定数据库的语言 -->
<property name="dialect">
org.hibernate.dialect.MySQlInnoDialect
</property>
<!-- 根据需要自动创建数据表 -->
<property name="hbm2ddl.auto">
create
</property>
<!-- 罗列所有的映射文件-->
<mapping resource="News.hbm.xml">
</session-factory>
</hibernate-configuration>
Hibernate的配置文件的默认文件名为hibernate.cfg.xml,当程序调用Configuration对象的configure()方法时,Hibernate 将自动加载该文件。
Hibernate配置文件同样也是一个XML文件,同样该文件的第一行也是XML文件声明,指定该文件的版本和编码所用的字符集。
Hibernate配置文件的根元素hibernate-configuration,根元素里有session-factory子元素,该元素依次有很多的property子元素,这些property元素Hibernate连接数据库的必要信息,如连接数据库驱动、URL、用户名、密码等信息。
除此之外,HIbernate 并不推荐使用DriverManager来连接数据库,而是推荐使用数据源来管理数据库连接、这样能够保证最好的性能,Hibernate推荐使用c3p0数据源。
提示:数据源是一种提高数据库连接性能的常规手段,数据源会负责一个维持一个数据连接池,当程序创建数据源实例时,系统会一次性创建多个数据库连接,并把这些数据库连接保存在数据连接池中,当程序需要进行数据库访问时,无需重新获得数据库连接,而是从数据库连接池中取出一个空闲的数据库连接。当程序使用数据库连接访问数据库结束之后,无须关闭数据库连接,而是将数据库连接归还给数据库连接池即可。通过这种方式就可以避免频繁地获取数据库连接、关闭数据库连接所导致的性能下降。
上面的配置文件的第53行指定了hbm2ddl.auto属性,该属性指定是否需要Hibernate根据映射文件来自动创建数据表——本应用指定create,即表示Hibernate会根据映射文件创建数据库表。
<session-factory .../>元素还可以接受多个<mapping../>元素,每个mapping元素指定一个映射文件,<mapping../>元素可以指定一个resource属性,该属性指定Hibernate映射文件的文件名。如有多个持久化映射文件,在此处罗列多个<mapping../>元素即可。
以下是在News类中完成消息插入的代码:
package ppp;
import javax.security.auth.login.Configuration;
public class NewsManager {
public static void main(String[] args) {
//实例化Configuration,这行代码默认加载hibernate.cfg.xml文件
Configuration conf = new Configuration().configure();
//以Configuration创建SessionFactory
SessionFactory sf = conf.buildSessionFactory();
//实例化Session
Session sess = sf.openSession();
//开始事务
Transaction tx = sess.beginTransaction();
//创建消息实例
News n = new News();
//设置消息标题和消息内容
n.setTitle("中国解放了");
n.setContent("中国解放了");
//保存消息
sess.save(n);
//提示事务
tx.commit();
//关闭Session
sess.close();
}
}
以上的持久化代码非常简单,保存消息只需要16至19行代码:程序先创建一个News对象,再使用Session的save方法保存News对象即可,这是完全对象化的操作方式,可以说是非常的简单、明了。当Java程序以面向对象的方式来操作持久化对象时,Hibernate负责将这种操作转换为底层的SQL操作。
当上面的程序运行结束后,我们可以看到Hibernate数据库中多了个数据表:news_table,且该表中包含了News实例所对应的记录。上面代码的8、10、12、14行显示:执行了session.save(News)之前,先要获取Session对象。PO只有在Session 管理下才能完成数据库访问。为了使用Hibernate进行持久化操作,通常有如下的操作步骤:
(1)开发持久化类,由POJO加映射文件组成。
(2)获取Configuration。
(3)获取SessionFactory。
(4)获取Session,打开事务。
(5)用面向对象方式操作数据库。
(6)关闭事务,关闭Session。
随PO和Session的关联关系,PO可有以下的3种状态。
(1)瞬态:如果PO实例从未与Session实例关联过,该实例处于瞬态状态。
(2)持久化:如果PO 实例与Session实例关联起来,且该实例关联到数据库的记录。
(3)脱管:如果PO实例曾经与Session实例关联过,但是因为Session的关闭等原因,PO实例脱离了Session 的管理,这种状态被称为脱管状态。
对PO的操作必须在Session的管理下才能同步到数据库,Session由SessionFactory 工厂产生,SessionFactory是数据库编译后的内存镜像,通常一个对应一个SessionFactory对象。SessionFactory对象是由Configuration对象生成,Configuration负责加载Hibernate配置文件。
上面的使用Hibernate添加了一条记录,对比Hibernate和JDBC两种操作数据库的方式,不难发现Hibernate有两个显著的优点。
(1)不再需要编写SQL语句,而是允许采用OO方式来访问数据库。
(2)JDBC访问过程中大量的checked异常被包装成Hibernate的Runtime异常,从而不再要求程序必须处理所有异常。