利用ThreadLocal绑定Hibernate的session
如果不用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(); }