Hibernate学习笔记
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页面,然后看看数据库中是否多了一条记录,这表明例程运行成功。