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。