转载:http://tech.it168.com/j/2006-11-03/200611030910932_2.shtml
【IT168 技术文档】本文主要讨论如何使用Hibernate处理会话和事务,并且讨论了会话和事务为什么对于数据库应用程序是非常重要的,最后描述了Hibernate中进行会话和事务管理的核心API。
在数据库应用程序中最基本的操作是CRUD(创建/读取/更新/删除),这4个操作构成了数据库应用程序的基石。在单机时代,一个数据库只由一个应用程序使用,这样CRUD操作并不会带来什么负面影响,但当进入网络时代后,这一切都发生了改变。由于网络数据库程序一般是由多个客户端同时操作一个数据库,因此,传统的CRUD操作将变得不再安全。因此,这个问题将引出本文的中心内容:事务。通过事务可以使传统的CRUD操作适应网络数据库应用。
本文分为三个部分。第一部分讨论了Hibernate如何管理事务和会话。第二部分描述了Hibernate用于管理事务和会话的核心API。在第三部分将举一个如何应用会话和事务的例子,并讨论了ThreadLocal变量。
Hibernate中的会话和事务
所谓会话,就是一个客户端从登录服务器开始,到登出服务器为止,在这期间关于客户端和服务器之间的所有活动。一个会话中可包含一个或多个事务,每一个事务是一原子操作,这个原子操作中可以包含多个操作,但这些操作是一个整体,要么都成功,要么都失败。在讨论Hibernate的会话和事务之前,让我们先来了解一下事务在底层是如何工作的。在Java中的操作数据库一般是和JDBC直接打交道,而JDBC则是直接通过Socket和数据库打交道。我们所说的底层实际上就是指JDBC和数据库层。
首先,让我们来看看数据库事务。数据库的操作由很多CRUD操作集合组成。而每一个集合就是数据库的一个事务。一个事务由begin transaction开始,最后以commit或rollback结束,这两个事务结束语保证了这一组CRUD操作要么都成功执行,要么回到执行前的状态。换句话说,这一组CRUD操作只要有一条语句执行失败,整个组都将回滚。这么做是为了保证数据的完整性。
如果事务中只有一个操作,那么事务是否提交,只依赖于这个操作的成功或失败。如一条INSERT语句,只有在这条语句成功执行后,数据才被真正写到数据库中。
现在让我们来看看事务中包含多条CRUD语句的情况,如包含一条UPDATE语句和二条DELETE语句。在这种情况下,不管这三条语句哪一条执行失败,整个事务都将回滚。
在使用事务时为了不产生负面影响,常常要对事务进行划分,即将一个大的事务合理划分成一些小的事务。这样如果某一个事务回滚的话,就不用回滚所有操作了。至于将大事务划分成多少个小的事务,这就要根据具体的情况而定
如果事务中只有一个操作,那么事务是否提交,只依赖于这个操作的成功或失败。如一条INSERT语句,只有在这条语句成功执行后,数据才被真正写到数据库中。
现在让我们来看看事务中包含多条CRUD语句的情况,如包含一条UPDATE语句和二条DELETE语句。在这种情况下,不管这三条语句哪一条执行失败,整个事务都将回滚。
在使用事务时为了不产生负面影响,常常要对事务进行划分,即将一个大的事务合理划分成一些小的事务。这样如果某一个事务回滚的话,就不用回滚所有操作了。至于将大事务划分成多少个小的事务,这就要根据具体的情况而定。
现在让我们来看看JDBC事务。对于数据库事务而言,操作事务的语言一般是SQL。而在编程环境中,事务要分为两种类型,非管制事务和管制事务。对于非管制事务,我们需要直接使用JDBC API来操作。我们可以使用三个JDBC方法来操作事务,它们是setAutoCommit(false)、commit()和rollback()。使用方法如下面的代码所示:
1Try 2
{ 3
conn.setAutoCommit(false); // 参数要使用false,否则将自动提交 4
// 连接和操作数据库 5
conn.commit(); 6
} 7
catch (Exception e) 8
{ 9
conn.rollback(); 10
} 11
![]()
为了使Hibernate更有吸引力,Hibernate为我们提供了会话,一个会话可以包含一个或多个事务。而Hibernate提供了很多处理会话和事务的API。这些API可以使我们更方便地处理事务,这就是我们下一部分要讨论的:用于管理事务的核心API。
管理事务的核心API
在Hibernate中,事务的核心是事务接口。实现这些接口的类负责处理相关的工作,如通讯、在数据库或JTA层的事务管理。一个事务对象总是被包含在会话对象中。我们可以通过会话对象中的beginTransaction()来实例化一个事务对象。如下如示:
Transaction transaction = session.beginTransaction(); // 实例化一个事务对象
上面的语句将实例化一个叫transaction的事务对象。在Hibernate中提供了三种事务,如下如示:
1. JDBC事务
2. JTA事务
3. CMT事务
在这三种事务中,JDBC事务是默认的。这就意味着如果未设置hibernate.properties文件中的hibernate.transaction_factory的键值,beginTransaction将返回一个JDBC事务。
虽然一个会话中可以包含多个事务,但并不需要人为地对这些事务进行分割,而是由会话中的一些方法对多个事务进行管理的。下面将详细描述这些方法:
commit()方法是结束事务的两个方法之一。在这方法在内部调用了会话对象的save()方法。当调用这个方法时,如果这个事务已经被初始化,那么这个事务将成功提交。
rollback()方法从它的名字可以看出,这个方法将事务恢复到执行前的状态。初始化一个事务和提交或回滚事务的代码如下:
try
{
Transaction transaction = session.beginTransaction();
// 对数据库进行CRUD 操作
transaction.commit(); // 提交事务
}
catch (Exception e)
{
transaction.rollback(); // 回滚事务
}
![]()
上述的几个方法是Hibernate处理事务的核心方法。在下一部分我们将实现一个工厂类,这个类封装了Hibernate用于管理事务的功能,从而使代码得到重用。
示例
到现在为止,我们已经学会如何设置、建立会话和得到一个事务。接下来我们将建立一个类,这个类将封装Hibernate框架的一些功能,从而使代码得到重用。在实现这个类之前,让我们先来学习一下什么是ThreadLocal变量。
ThreadLocal变量和一般的通过get或set方法访问的变量不同。ThreadLocal变量是类中的私有静态(private static)变量。在我们的例子中,这个变量就是会话对象。
上面给出了ThreadLocal变量的概念,现在让我们来举个例子看看这个变量到底是什么。我们把要实现的类命名为MyHibernate。
下面是必须引用的包
import org.hibernate. * ;
import org.hibernate.cfg. * ;
![]()
下面是要实现的类的框架
![]()
public class MyHibernate
{
… …
}
![]()
由于这个类使用的是工厂模式,因此,所有的方法必须是静态的,而且没有构造函数。虽然没有构造函数,但必须在static块中初始化。
public class MyHibernate
{
public static final SessionFactory sessionFactory;
![]()
static
{
try
{
// 根据hibernate.cfg.xml建立SessionFactory对象
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch (Throwable e)
{
System.err.println("初始化错误:” + e.getMessage());
throw new ExceptionInInitializerError(e);
}
}
… …
}
![]()
接下来声明ThreadLocal变量
public class MyHibernate {
… …
public static final ThreadLocal session = new ThreadLocal();
… …
}
![]()
现在来实现管理会话的方法
![]()
public class MyHibernate
{
… …
public static Session currentSession() throws HibernateException
{
Session mySession = (Session) session.get(); // 得到一个会话实例
// 如果得到一个空的会话,建立一个新的会话
if (mySession == null)
{
mySession = sessionFactory.openSession();
// 将这个会话保存在ThreadLocal变量中
session.set(mySession);
}
return mySession;
}
![]()
public static void closeSession() throws HibernateException
{
Session mySession = (Session) session.get();
if (mySession != null)
mySession.close();
session.set(null);
}
… …
}
![]()
最后,我们来实现控制事务的方法。
public class MyHibernate
{
… …
// 返回会话工厂
public static SessionFactory getSessionFactory( )
{
return sessionFactory;
}
// 回滚方法
public static void rollback(Transaction transaction)
{
if (transaction != null)
{
try
{
transaction.rollback( );
}
catch (HibernateException e) {}
}
}
// 提交事务的方法
public static void commit(Transaction transaction)
{
if (transaction != null)
{
try {
transaction.commit( );
}
catch (HibernateException e) {}
}
}
}
![]()
这个类可以使用在桌面程序中,也可以使用在Web程序中。但要注意,在Web程序中使用它时,要将hibernate.cfg.xml文件放到classes目录中。下面是一个使用这个类的例子。
import java.util. * ;
import com.someorg.persist. * ;
import org.hibernate. * ;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion. * ;
![]()
public class TestMyHibernate {
![]()
HibernateFactory hf;
![]()
public TestMyHibernate () {}
public Info getInfo(String lower, String upper)
{
// 打开一个会话
Session sess = hf.getSession();
![]()
List list = sess.createCriteria(Info.class).add(Restrictions.between(lower,upper).list();
Info o=(Info)list.iterator.next(); // 得到第一条记录
hf.closeSession();
return o;
}
![]()
public static void main(String args[]){
Info info = TestMyHibernate().getInfo(“10”, “100”);
System.out.println(“Info Id:”+ info.id);
… …
}
}