应用背景:
我们知道java web项目开发中不可避免的是连接数据库,这里我们设定数据库的连接及其对数据库的查询、更新、修改为持久层的操作。我们在web项目开发中对持久层操作不可缺少的利器是Hibernate。对于hibernate是什么、背景楼主就不一一解释了。好的!让我们进入主题:
Hibernate里的Session为org.hibernate.Session,代表一次完整的数据库操作,与servlet里的Session完全不同。一次Session操作可以包括多次的数据库的读写、多个事务。我们知道Session是由SessionFactory负责创建的,而多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么Session是非线程安全的,由此背景我们引入了ThreadLocal。
1.ThreadLocal是什么?原理?
其实ThreadLocal并不是一个Thread,而是thread local variable(线程局部变量)。
在运用中为避免资源消耗,一般都会手动封装一个HibernateUtil类(未使用Spring管理的前提下)。该类的作用使Hibernate加载配置文件config, 创建sessionFactory等只运行一次。
实际运用中,经常需要将当前线程和session绑定.一般的用法为使用ThreadLocal:在HibernateUtil类中封装hibernate的管理。通过openSession取得session,并将其放入ThreadLocal变量中。这样业务逻辑中仅需通过工具类取得当前线程对应的session。使用完毕后,调用工具类closeSession方法将session关闭,当前线程的ThreadLocal变量置为NULL。保证线程归还线程池复用后,ThreadLocal为空,以免出现导致其他线程访问到本线程变量。
而后,Hibernate的SessionFactory提供获取session的新方法getCurrentSession (获得与当前线程绑定的session)。内部通过代理封装,此方式得到的session不仅和当前线程绑定,也无需手动开关。默认在事务提交之后,session自动关闭。需注意的是,必须在事务开启的前提之下才可使用此种方式获得的session。此外hibernate.cfg.xml配置文件中也需配置
<propertyname="current_session_context_class">thread</property>
最后,引入Spring之后。sessionfactory的创建等都交给spring管理。Spring也提供了HibernateTemplate,HibernateDaoSupport这样的封装方法。用户可以不再考虑session的管理,事务的开启关闭。只需配置事务即可。而所谓session关闭后,因延迟加载导致前台无法显示的问题以往解决方式为强制全部加载,现在也可通过在web.xml中配置org.springframework.orm.hibernate3.support.OpenSessionInViewFilter来解决。
总的来讲就是给session加了一把锁机制,防止并发访问时出现错误。
{ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new对象的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的。}这个是别人的观点!!!//倒数第二个图标去除背景,汗!!!
2. ThreadLocal管理hibernate的Session实例
HibernateUtil类:
package com.jc.util; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; public class HibernateUtil { // 声明SessionFactory对象 private static SessionFactory factory = null; // 实例化ThreadLocal对象 private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>(); // 实例化Configuration对象 private static Configuration cfg = new Configuration(); // 静态块 static { try { // 加载Hibernate配置文件 cfg.configure(); // 实例化SessionFactory factory = cfg.buildSessionFactory(); } catch (HibernateException e) { e.printStackTrace(); // 打印异常信息 } } /** * 获取Session对象 * @return Session对象 */ public static Session getSession() { // 从threadLocal中获取Session Session session = (Session) threadLocal.get(); // 判断session是否为空或未处于开启状态 if (session == null || !session.isOpen()) { if (factory == null) { rebuildSessionFactory(); } // 从factory开启一个Session session = (factory != null) ? factory.openSession() : null; threadLocal.set(session); // 将session放入threadLocal中 } return session; } /** * 获取SessionFactory对象 * @return SessionFactory对象 */ public static SessionFactory getSessionFactory() { return factory; } /** * 关闭Session * @param session对象 */ public static void closeSession() { // 从threadLocal中获取Session Session session = (Session) threadLocal.get(); // 移除threadLocal中的对象 threadLocal.remove(); if (session != null) { if (session.isOpen()) { session.close(); // 关闭Session } } } /** * 创建SessionFactory对象 */ public static void rebuildSessionFactory() { try { // 加载Hibernate配置文件 cfg.configure(); // 实例化SessionFactory factory = cfg.buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); // 打印异常信息 } } }
Dao层包:
import org.hibernate.Query; import org.hibernate.Session; import com.jc.model.User; import com.jc.util.HibernateUtil; /** * 用户数据库处理类 */ public class UserDao { /** * 保存用户 * @param user User对象 */ public void saveUser(User user){ Session session = null; //Session对象 try { //获取Session session = HibernateUtil.getSession(); session.beginTransaction(); //开启事物 session.save(user); //持久化user session.getTransaction().commit(); //提交事物 } catch (Exception e) { e.printStackTrace(); //打印异常信息 session.getTransaction().rollback();//回滚事物 }finally{ HibernateUtil.closeSession(); //关闭Session } } }
这里线程A首先使用Dao包中的UserDao类 session = HibernateUtil.getSession();将会执行HibernateUtil类中static块创建一个factory,之后执行里面的getSession()方法,当session = (factory != null) ? factory.openSession() : null;语句执行完后,session打开。之后的threadLocal.set(session);会将session保存到threadLocal里。
之后来了线程B,同样使用Dao包中的UserDao类 session = HibernateUtil.getSession();但是在执行到getsession()里面时,我们会发现通过Session session = (Session) threadLocal.get();获得的session是非空的,之后if (session == null || !session.isOpen())两个条件均不成立(A正在使用),故里面语句不执行,session并发访问控制得到很好解决,谢谢阅读!