java web 解析ThreadLocal在持久层中的应用

来源:互联网 发布:淘宝上有修键盘的吗 编辑:程序博客网 时间:2024/06/07 01:24

应用背景:

我们知道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管理hibernateSession实例


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();// 实例化SessionFactoryfactory = cfg.buildSessionFactory();} catch (HibernateException e) {e.printStackTrace(); // 打印异常信息}}/** * 获取Session对象 * @return Session对象 */public static Session getSession() {// 从threadLocal中获取SessionSession session = (Session) threadLocal.get();// 判断session是否为空或未处于开启状态if (session == null || !session.isOpen()) {if (factory == null) {rebuildSessionFactory();}// 从factory开启一个Sessionsession = (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中获取SessionSession 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();// 实例化SessionFactoryfactory = 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 {//获取Sessionsession = HibernateUtil.getSession();session.beginTransaction();//开启事物session.save(user);//持久化usersession.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并发访问控制得到很好解决,谢谢阅读!