1. Hibernate介绍
Hibernate是一个开放源代码的O/R Mapping (对象关系映射框架),它对JDBC进行了轻量级的对象封装,使Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
Hibernate 是一个面向Java 环境的对象/关系数据库映射工具。对象/关系数据库映射(object/relational mapping (ORM))这个术语表示一种技术,用来把对象模型表示的对象映射到基于SQL 的关系模型结构中去。Hibernate 不仅仅管理Java 类到数据库表的映射,还提供数据查询和获取数据的方法,可以大
幅度减少开发时人工使用SQL 和JDBC 处理数据的时间。Hibernate 的目标是对于开发者通常的数据持久化相关的编程任务,解放其中的95%。
Hibernate开发准备工作需要准备工具:
Hibernate工具,下载地址:http://www.hibernate.org.cn/download/
hibernate-2.1.2.zip,然后将其解压缩。假设放到C:\hibernate-2.1目录下(我们在这里设置环境变量HIBERNATE_HOME= C:\hibernate-2.1)。让我们看看Hibernate的包情况,首先在%HIBERNATE_HOME%目录下有一个hibernate
2.jar,该包就是我们进行Hibernate开发所需要用到的Hibernate工具包,当然Hibernate本身还需要其他lib的支持,这些支持包都在%HIBERNATE_HO
ME%\lib下面。请看表1-1,Hibernate的第三方库
库名称 |
说明 |
dom4j (必需) |
Hibernate 在解析XML 配置和XML 映射元文件时需要使用dom4j。 |
CGLIB (必需) |
Hibernate 在运行时使用这个代码生成库强化类(与Java 反射机制联合使用)。 |
Commons collection,Commons logging |
Hibernat 使用Apache Jakarta Commons 项目提供的多个工具类库。
|
ODMG4 |
Hibernate 提供了一个可选的ODMG 兼容持久化管理界面。如果你需要映射集合,你就需要这个类库,就算你不是为了使用ODMG API。 |
Log4j |
Hibernate 使用Commons Logging API,后者可以使用Log4j 作为实施log 的机制。如果把Log4j 库放到上下文类目录中,Commons Logging就会使用Log4j 和它在上下文类路径中找到的log4j.properties 文件。在Hibernate 发行包中包含有一个示例的properties 文件。所以,也把log4j.jar 拷贝到你的上下文类路径去吧。 |
其他文件是不是必须的 |
请察看Hibernate 发行包中的/lib/README.txt 文件。这是一个
Hibernate 发行包中附带的第三方类库的列表,总是保持更新。你可以在那里找到所有必需或者可选的类库的列表。 |
表1-1
2. Hibernate的配置
首先,让我们看看Hibernate的全局配置文件,这个配置文件可以有两种形式,每种形式都可以完成Hibernate的配置工作,分别是hibernate.properties或者hibernate.cfg.xml,这个文件需要放到上下文路径下面(假设在一个Web应用下,需要放到WEB-INF/classes下面),但请注意,虽然hibernate官方说法是必须放到该目录下,其实是可以放在别的路径下面,只需要在buildSessionFactory的时候加入配置文件的路径就可以了。关于配置文件的内容,可以参看%HIBERNATE_HOME%\src目录中hibernate.properties文件,而关于其含义解释,可以参看:
http://www.javajia.com/article.php?id=901
http://www.hibernate.org.cn/53.html
在进入Hibernate例程之前还需要其他一些准备工作。下载应用服务器JBoss3.2.6,其下载地址为:
http://voxel.dl.sourceforge.net/sourceforge/jboss/jboss-3.2.6.zip
下载mysql5.0.0,其下载地址为:
http://mysql.linuxforum.net/Downloads/MySQL-5.0/mysql-5.0.0a-alpha-win.zip
以及mysl数据库的jdbc数据库连接包,下载地址:
http://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-3.1.4-beta.zip
当然,你还必须确定你已经安装了j2sdk1.4.2,如果没有请下载安装:
http://java.sun.com/j2sk/1.4.2/download.html
还有就是你必须确定,您已经将JAVA_HOME,JBOSS_HOME,classpath等环境变量设置齐全。最好将mysql的jdbc连接包,放到%JBOSS_HOME%\
server\default\lib下面。
此外,你还需要在mysql数据库中建立一个test库,并且在该库中建立一张名为courses的表,其创建语句为:
create database if not exists `test`;
use `test`;
drop table if exists `courses`;
CREATE TABLE `courses` (
`CourseId` varchar(32) NOT NULL default '',
`name` varchar(32) default NULL,
PRIMARY KEY (`CourseId`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `courses` VALUES
('2313213213','eeeeeee'),
('12345656','Jacky'),
('Bonnie','Bonnie');
这样就创建了我们例程所需要的表和测试数据。此外我们还需要配置JBoss的数据源,在此之前,请启动JBoss,如若不能正常启动,请查看环境变量配置情况,或者查看JBoss网站。找到%JBOSS_HOME%\server\default\deploy目录,查看该目录下是否有mysql-ds.xml的mysql数据源配置文件。如果没有请新建该文件,该文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/test</connection-url>
<driver-class>org.gjt.mm.mysql.Driver</driver-class>
<user-name>root</user-name>
<password></password>
</local-tx-datasource>
</datasources>
这样就配置好了Jboss的jdbc数据源。其名称为MySqlDS。启动JBoss,注意控制台输出信息,已经将数据源邦定到java:/MySqlDS的JNDI名称。
3. Hibernate例程
a) 打开JBuilder新建一个Project,将其命名为TestHibernate。然后
将%HIBERNATE_HOME%\hibernate2.jar以及%HIBERNATE_HOME%\lib下面所有以上提到的包(你也可以将其下所有的包)放到一个JBuilder library下,将这个library引入TestHibernate工程的classpath下面。这样就将所有hibernate所需要的包引入了。
b) 建立第一个可持久化类。新建一个类,将其package设com.hibernate
并且将其命名为Cours.java,其代码如下:
package com.hibernate;
import java.util.Set;
/**
*在hibernate中代表了Course表的类。
*/
public class Cours
{
/**每个属性和表的一个字段对应**/
private String courseId;
private String name;
/**students表示course中的学生,在后面才会用到,暂时不管**/
private Set students;
/**属性的访问方法**/
public void setCourseId(String string) {
courseId = string;
}
public String getCourseId() {
return courseId;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setStudents(Set stud){
this.students=stud;
}
public Set getStudents(){
return this.students;
}
}
c)建立一个Jsp页面以便JBuilder生成defaultroot目录结构,编译java源文件。
d)这个时候我们就可以找到该JBuilder工程所在的目录,找到defaultroot下面的classes目录,在该目录中手工建立一个Hibernate配置文件hibernate.cfg.xml,现在我们来看配置该文件。
<!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:/MySqlDS</property>
<property name="show_sql">false</property>
<property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>
<property name="jndi.class">org.jnp.interfaces.NamingContextFactory</property>
<property name="jndi.url">jnp://localhost:1099/</property>
<!-- Mapping files -->
<mapping resource="Cours.hbm.xml"/>
</session-factory>
</hibernate-configuration>
首先我们要确保Hibernate会连接到MySql数据库,参考前文提到的Hibernate配置文档,这是就需要我们配置hibernate.dialect方言属性,该属性说明hibernate需要连接的数据库类型,在此我们设置为MySql类型。将hibernate.dialect设置成net.sf.hibernate.dialect.MySQLDialect。
而访问数据库的数据源,我们在此设置成了java:/MySqlDS。我们需要注意,Hibernate自带一个连接池配置方案(hibernate.properties方案):
hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql:///test
hibernate.connection.username root
hibernate.connection.password
如果你去研究Hibernate的源代码,会发现Hibernate自带的连接池方案是一个非常原始的连接池,所以我们在此不用。Hibernate总共有三种连接吃方案:Hibernate自带连接池、Hibernate带的DBCP连接池、App Server的连接池。当然,我们选择的是最后一种方案,采用App Server提供连接池的方案。
而hibernate.show_sql属性是让用户能够购在调试程序的时候通过控制台看到sql语句的输出,以便用户调试,在这里我们选择了关闭该输出,当然你也可以打开该输出,只要将该属性设置为true值。
对于hibernate.jndi.class和hibernate.jndi.url属性,该属性是当你如果想从远程客户端直接调用处在不同的JVM下的资源的时候,你就需要通过这两个属性查找JNDI资源,这里设置的是通过jnp提供的NamingContextFactory来查找jnp://localhost:1099这个服务下的资源。但是需要注意的是:JBoss配置的DataSource的JNDI名称是java:/MySqlDS,JBoss是不支持远程客户端对其JNDI资源的调用。所以在这里这两个属性并没有实际意义,而当我们将JBoss换成如Weblogic等的应用服务器的时候,就可以在远程客户端通过查找JNDI名称来获得处于另外JVM下的服务器资源,例如连接池数据源。
对于mapping映射属性,这是通知hibernate,让其知道现在已经有一个映射文件来映射数据库中的表,这个文件名是Cours.hbm.xml。
有关hibernate.cfg.xml或者hibernate.properties文件的更多配置信息,请参看前文提到的参考地址。
e)下面让我们看看上节提到的Cours.hbm.xml,同样,这个文件需要
放到defaultroot\classes目录下。该文件的内容如下:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
<class
name="com.hibernate.Cours"
table="Courses"
dynamic-update="false"
>
<id
name="courseId"
column="CourseId"
type="string"
unsaved-value="any"
>
<generator class="assigned"/>
</id>
<property
name="name"
type="string"
update="true"
insert="true"
column="Name"
/>
</class>
</hibernate-mapping>
首先让我们来看class节点,该节点描述数据库中的一个表对应我们创建的一个可持久化类(该持久化类类似于JavaBean,通过getter和setter方法访问)。在这里我们可以看到name为com.hibernate.Cours的可持久化类对应于table为Courses的数据库表,dynamic-update属性用来标识是否对update的sql语句在运行时动态生成,并且之更新那些改变过的字段。在这里我们不需要过于关注这个属性。
其次,观察id子节点,该节点表示了表的关键字段,name=”courseId”表明了com.hibernate.Cours类中的对应courses表主关键字的attribute属性是courseId。而对应的字段是column="CourseId",类型为type="string"
而对于unsaved-value="any" 一个特定的标识属性值,用来标志该实例是刚
刚创建的,尚未保存。这可以把这种实例和从以前的session 中装载过但未再次持久化的实例区分开来。<generator>节点定义该主关键字的生成规则,assign表示该主关键字生成规则为用户在save之前为对象分配主关键字标识符。关键字生成规则有以下几种类型:
increment(递增)
用于为long, short 或者int 类型生成唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。在集群下不要使用。
Identity
对DB2,MySQL, MS SQL Server, Sybase 和HypersonicSQL 的内置标识字段提供支持。返回的标识符是long, short 或者int 类型的。
sequence (序列)
在DB2,PostgreSQL, Oracle, SAP DB, McKoi 中使用序列(sequence),而在Interbase 中使用生成器(generator)。返回的标识符是long, short 或者 int 类型的。
hilo (高低位)
使用一个高/低位算法来高效的生成long, short 或者 int 类型的标识符。给定一个表和字段(默认分别是是hibernate_unique_key 和next)作为高位值得来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。在使用JTA 获得的连接或者用户自行提供的连接中,不要使用这种生成器。
seqhilo(使用序列的高低位)
使用一个高/低位算法来高效的生成long, short 或者 int 类型的标识符,给定一个数据库序列(sequence)的名字。
uuid.hex
用一个128-bit 的UUID 算法生成字符串类型的标识符。在一个网络中唯一(使用了IP地址)。UUID 被编码为一个32 位16 进制数字的字符串。
uuid.string
使用同样的UUID 算法。UUID 被编码为一个16 个字符长的任意ASCII 字符组成的字符串。不能使用在PostgreSQL 数据库中
native(本地)
根据底层数据库的能力选择identity, sequence 或者hilo 中的一个。
assigned(程序设置)
让应用程序在save()之前为对象分配一个标示符。
foreign(外部引用)
使用另外一个相关联的对象的标识符。和<one-to-one>联合一起使用。
Hibernate用property节点来描述其他非关键字字段的属性,
f)我们需要创建一个HibernateUtil类,来作为获取Session的工具类。
我们现在可以开始Hibernate 的Session 了。我们用它来从数据库中存取Cours。首先,我们要从SessionFactory 中获取一个Session(Hibernate 的工作单元)。
SessionFactory sessionFactory =
new Configuration().configure().buildSessionFactory();
SessionFactory 负责一个数据库,也只对应一个XML 配置文件(hibernate.cfg.xml)。你可以用喜欢的任何方式编写一个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();
Cours course = new Cours();
course.setCourseId("aaaaaaa");
course.setName(“bbbbbbb”)
session.save(princess);
tx.commit();
HibernateUtil.closeSession();
在Session 中,每个数据库操作都是在一个事务(transaction)中进行的,这样就可以隔离开不同的操作(甚至包括只读操作)。我们使用Hibernate 的Transaction API 来从底层的事务策略中(本例中是JDBC 事务)脱身。这样,如果需要把我们的程序部署到一个由容器管理事务
的环境中去(使用JTA),我们就不需要更改源代码。请注意,我们上面的例子没有处理任何异常。也请注意,你可以随心所欲的多次调用HibernateUtil.
currentSession(),你每次都会得到同一个当前线程的Session。你必须确保Session 在你的数据库事务完成后关闭,不管是在你的Servlet 代码中,或者在ServletFilter 中,HTTP 结果返回之前。
g)创建访问bean类,来封装调用hibernate访问数据库的方法。在这里我们暂且定义该方法为StoreAccessBean.java。源文件如下:
package com.hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import java.util.Iterator;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class StoreAccessBean{
public StoreAccessBean(){
}
public void addStoreAccess(Cours store){
try {
Session session = HibernateUtil.currentSession();
Transaction trans = session.beginTransaction();
session.save(store);
trans.commit();
HibernateUtil.closeSession();
}
catch (Exception ex) {
ex.printStackTrace();
}
}
public Iterator getAllCourses()throws HibernateException
{
String queryString = "select storeaccess from storeaccess as storeaccess";
Session session = HibernateUtil.currentSession();
net.sf.hibernate.Query query = session.createQuery(queryString);
Iterator it= query.iterate();
HibernateUtil.closeSession();
return it;
}
}
该类中有两个方法,一个是添加一条数据的方法,另外一个是查询的方法。
最后,编译所有文件。在刚才新建的jsp中加入以下代码:
com.hibernate.Cours access = new com.hibernate.Cours(); access.setCourseId("aaaaaaa");
access.setName("bbbbbb");
com.hibernate.StoreAccessBean bean = new com.hibernate.StoreAccessBean();
最后,打成war包,部署到%JBOSS_HOME%\server\default\deploy目录中,启动jboss,访问建立的jsp页面,然后看看数据库中是否多了一条记录,这表明例程运行成功。