利用ThreadLocal绑定Hibernate的session

天使 2010-08-16

如果不用spring,单用hibernate如何来解决延迟加载的问题.

无论是立即加载还是延迟加载必须要连接数据库的,而在java中连接数据库是依赖java.sql.Connection,在hibernate中session就是Connection的一层高级封装,一个session对应了一个Connection,要实现延迟加载必须有session才行.而且要进行延迟加载还必须保证是同一个session才行,用另外一个session去延迟加载前一个session的代理对象是不行的.大家都知道Connection是使用过后必须要进行关闭的,那么我们如何保证一次http请求过程中,一直都使用一个session呢,即一个Connection呢.而且还要保证http请求结束后正确的关闭.

好,现在我们知道了我们要解决的问题

1.如何保证http请求结束后正确的关闭session

2.如何保证http请求过程中一直使用同一个session

第一个问题很容易想到,使用过滤器

public void doFilter(ServletRequest request, ServletResponse response,  
  
        FilterChain filterChain) {  
  
    try {  
  
        filterChain.doFilter(request, response);  
  
    } catch (IOException e) {  
  
        e.printStackTrace();  
  
    } catch (ServletException e) {  
  
        e.printStackTrace();  
  
    } finally {  
  
        try {  
  
            HibernateUtil.commitTransaction();  
  
        } catch (Exception e) {  
  
            HibernateUtil.rollbackTransaction();  
  
        } finally {  
  
            HibernateUtil.closeSession();  
  
        }  
  
    }  
  
}

要解决第二个问题我们必须先搞清楚,http请求在java中是以什么样的机制实现的,在java中一个请求就是一个线程,像流行的web容器Tomcat等,往往都是采用线程池机制的也就是说有n个线程在池子里面,每当有http请求时,随机从线程池中取出一个线程对象去处理请求,实际上多次请求可能使用的是同一线程也可能不是,这是随机的.要保证整个请求中使用同一session最容易想到的就是把这个session绑定到线程上,在java中使用ThreadLocal可以轻松绑定变量,每个线程有一个自己的ThreadLocal,这个ThreadLocal会随线程的销毁一起销毁,既然是每个线程有一个那么多个线程间自然是不会有影响了,所以把session绑定在ThreadLocal里面是最好的选择了,

有关ThreadLocal的更多资料,大家可以百度或者参考

http://www-128.ibm.com/developerworks/cn/java/j-threads/index3.html

http://www.blogjava.net/jspark/archive/2006/08/01/61165.html

最后我把相关的代码发出来

import java.sql.SQLException;  
  
import org.hibernate.HibernateException;  
  
import org.hibernate.Session;  
  
import org.hibernate.SessionFactory;  
  
import org.hibernate.Transaction;  
  
import org.hibernate.cfg.Configuration;  
  
import java.sql.Connection;  
  
import org.apache.log4j.Logger;  
  
import java.io.File;  
  
  
  
/** 
 
 *  
 
 * <p> 
 
 * Title:Hibernate工具类 
 
 * </p> 
 
 *  
 
 * <p> 
 
 * 利用ThreadLocal 绑定 Hibernate 的session 
 
 * </p> 
 
 *  
 
 * @author 孙钰佳  
 
 * @mail [email protected] 
 
 * @version 1.0 
 
 */  
  
public class HibernateUtil {  
  
    /** 
 
     * Loger4j的logger 
 
     */  
  
    private static final Logger logger = Logger.getLogger(HibernateUtil.class);  
  
    /** 
 
     * 存储hibernate session的ThreadLocal 
 
     */  
  
    private static final ThreadLocal sessionThread = new ThreadLocal();  
  
    /** 
 
     * 存储事务的ThreadLocal 
 
     */  
  
    private static final ThreadLocal transactionThread = new ThreadLocal();  
  
    /** 
 
     * Hibernate 的 Session工厂 
 
     */  
  
    private static SessionFactory sessionFactory = null;  
  
  
  
    /** 
 
     * 初始化SessionFactory 
 
     *  
 
     * @param file 
 
     *            Hibernate配置文件 
 
     */  
  
    public static void initSessionFactory(File file) {  
  
        Configuration config = new Configuration();  
  
        config.configure(file);  
  
        sessionFactory = config.buildSessionFactory();  
  
    }  
  
  
  
    /** 
 
     * 获取当前线程绑定的session 
 
     *  
 
     * @return Session 
 
     * @throws HibernateException 
 
     */  
  
    public static Session getSession() {  
  
        Session s = (Session) sessionThread.get();  
  
        if (s == null) {  
  
            s = sessionFactory.openSession();  
  
            sessionThread.set(s);  
  
        } else {  
  
            Connection conn = s.connection();  
  
            try {  
  
                if (conn == null || conn.isClosed()) {  
  
                    try {  
  
                        s.close();  
  
                    } catch (HibernateException e) {  
  
                        logger.warn("close session error:" + e.getMessage(), e);  
  
                    }  
  
                    s = sessionFactory.openSession();  
  
                    sessionThread.set(s);  
  
                }  
  
            } catch (SQLException e) {  
  
                throw new HibernateException(e);  
  
            }  
  
        }  
  
        return s;  
  
    }  
  
  
  
    /** 
 
     * 取得当前session的事务 
 
     *  
 
     * @return Transaction 
 
     */  
  
    public static Transaction transaction() {  
  
        Transaction transaction = (Transaction) transactionThread.get();  
  
        if (transaction == null) {  
  
            transaction = getSession().beginTransaction();  
  
            transactionThread.set(transaction);  
  
        }  
  
        return transaction;  
  
    }  
  
  
  
    /** 
 
     * 提交当前session的事务 
 
     */  
  
    public static void commitTransaction() {  
  
        Transaction transaction = (Transaction) transactionThread.get();  
  
        transactionThread.set(null);  
  
        if (transaction != null)  
  
            transaction.commit();  
  
    }  
  
  
  
    /** 
 
     * 回滚当前session的事务 
 
     */  
  
    public static void rollbackTransaction() {  
  
        Transaction tx = (Transaction) transactionThread.get();  
  
        transactionThread.set(null);  
  
        if (tx != null)  
  
            tx.rollback();  
  
    }  
  
  
  
    /** 
 
     * 关闭当前线程使用的session 
 
     */  
  
    public static void closeSession() {  
  
        Session session = (Session) sessionThread.get();  
  
        if (session != null) {  
  
            session.clear();  
  
            session.close();  
  
            sessionThread.set(null);  
  
        }  
  
    }  
  
}

下面是一个调用的例子:

public static void main(String[] args) throws Exception {  
  
    HibernateUtil.initSessionFactory(new File(Test.class.getClassLoader()  
  
            .getResource("hibernate.cfg.xml").getFile()));  
  
    Session session = HibernateUtil.getSession();  
  
    HibernateUtil.transaction();  
  
    User u = new User();  
  
    u.setName("test");  
  
    session.save(u);  
  
    HibernateUtil.commitTransaction();  
  
    HibernateUtil.closeSession();  
  
}

相关推荐

LetonLIU / 0评论 2020-05-29