hibernate事务和Session



hibernate事务和Session实例代码讲解。

1、Session和用户请求是一对一得关系,这是一种理想的Session管理模式。

推荐使用一个ThreadLocal变量,把Session绑定到处理客户端请求的线程上。这种方式可以让运行在该线程上的所有程序代码轻松地访问Session。也可以在一个ThreadLocal变量中保持事务上下文环境,不过这依赖于你所选择的数据库事务划分机制。这种实现模式被称为ThreadLocal Session和Open Session in View。

工具类代码如下:
public class HibernateUtil

{

//使用一个final变量来保存不可变的SessionFactory

public static final SessionFactory sessionFactory;

static{

try{

//采用默认的Hibernate.cfg,xml来启动一个Configuration的实例

Configuration conf = new Configuration().configure();

//由conf实例创建一个SessionFactory实例

sessionFactory = conf.buildSessionFactory();

}catch(Throwable ex){

System.err.println(“初始化SessionFactory出现异常:”+ex);

throw new ExceptionInitializerError(ex);

}

}

//ThreadLocal是隔离多个线程的数据共享

//不存在多个线程之间共享资源,因此不再需要对线程同步

public static final ThreadLocal session = new ThreadLocal();

public static Session currentSession() throws HibernateException

{

Session s = (Session)session.get();

//如果该线程还没有Session,则创建一个新的Session

if(s == null)

{

s = sessionFactory.openSession();

//将获得的Session变量存储在ThreadLocal变量session里

session.set(s);

}

return s;

}

public static void closeSession() throws HibernateException


{

Session s = (Session)session.get();

if(s != null)

{

s.close();

session.set(null);

}

}

}

Hibernate Session被绑定到当前线程。当调用currentSession方法时,如果当前线程中的Session已经创建出来,那么将返回这个已经存在的Session实例。

注意:几乎所有情况下,都不要使用每个应用对应一次Hibernate Session的模式,也不要使用每次Http Session对应一次Hibernate Session的模式。

2、对于以上的情况,Hibernate主要有如下3种模式来解决这个问题:

(1)自动版本化:Hibernate能够自动进行乐观并发控制,如果在用户思考的过程中持久化实体发生并发修改,Hibernate能够自动检测到。

(2)脱管现象:如果采用每次用户请求对应一次Session的模式,那么,前面载入的实例在用户思考的过程中,始终与Session脱离,处于脱管状态。Hibernate允许把脱管对象重新关联到Session上,并且对修改进行持久化。在这种模式下,自动版本化被用来隔离并发修改。这种模式也被称为脱管对象的每次请求对应一个Hibernate Session。

(3)长生命周期Session:Session可以在数据库事务提交之后,断开和底层的JDBC连接。当新的客户端请求到来时,它又重新连接上底层的JDBC链接。这种模式被称为每个应用程序事务对应一个Session。因为应用程序事务时相当长(跨越多个用户请求)的,所以也被称为长生命周期Session。

注意:Session缓存了处于持久化状态的每个对象(Hibernate会监视和检查脏数据),也就是说,如果程序让Session打开很长一段时间,或者载入了很多数据,Session占用的内存会一直增长,直到抛出OutOfMemoryException异常。为了解决这个问题,程序定期调用Session的clear()和evict()方法来管理Session的缓存。对于一些大批量的数据处理,推荐使用DML风格的HQL语句完成。

3、为了保证在Session关闭之前初始化代理属性或集合属性,程序可以Hibernate,initialized()静态方法来强制初始化集合或代理。只要Session处于open状态,Hibernate.initialize(teahcer)将会强制初始化teacher代理,Hibernate.initialize(teacher.getStudents())对student的集合具有同样的功能。

还有另一种选择,就是程序让Session一直处于打开状态,直到装入所有需要的集合或代理。

保证Session处于打开状态有两种方法可以解决此问题:

(1)在一个Web应用中,可以利用过滤器(Filter),在用户请求结束、页面生成结束时关闭Session。也就是保证在视图层一直打开Session,这就是所谓的Open Session in View成视图界面的过程中发生异常时,必须保证可以正确关闭Session,并结束事务。

(2)使用业务逻辑层来准备数据,在业务逻辑层返回数据之前,业务逻辑层对每个所需集合调用Hibernate.initialize()方法,或者使用带fetch子句或FetchMethod.JOIN的查询,事先取得所有数据,并将这些数据封装成VO(值对象)集合,然后程序可以关闭Session了。业务逻辑层VO集传入视图层,让视图层只负责简单的显示逻辑。在这种模式下,可以让视图层和Hibernate API彻底分离,保证视图层不会出现持久层API,从而提供更好的解耦。

上下文相关的Session

从Hibernate3.1开始,SessionFactory.getCurrentSession()的底层实现是可插拔的,Hibernate引入了CurrentSessionContext接口,并通过hibernate_current_session_context_class参数来管理上下文相关Session的底层实现。

CurrentSessionContext接口有如下3个实现类:

org.hibernate.context.JTASessionContext:根据JTA来跟踪和界定上下文相关Session,这和最早的仅支持JTA的方法是完全一样的。

org.hibernate.context.ThreadLocalSessionContext:通过当前正在执行的线程来跟踪和界定上下文相关Session,也就是和前面的HibernateUtil的Session维护模式相似。

org.hibernate.context.ManagedSessionContext:通过当前执行的线程来跟踪和界定上下文相关的Session。但是程序需要使用这个类的静态方法将Session实例绑定、取消绑定,它并不会自动打开、flush或者关闭任何Session。

对于容器中使用Hibernate的场景而言,通常会采用第一种方式:对于独立的Hibernate应用而言,通常会采用第二种方式。

为了指定Hibernate使用哪种Session管理方式,可以在hibernate.cfg.xml文件中增加如下片段:

<property name=”hibernate.current_session_context_class”>thread</property>

如果在JTA事务环境中,则应增加如下配置片段:

<property name=”hibernate.current_session_context_class”>jta</property>

对于第三种不常用的Session管理机制,则可在配置文件中简写成:managed。