用Hibernate的人都知道Hibernate最原始的使用Session方式(异常忽略):
获取SessionFactory 打开Session 打开事务(可选) 执行操作 关闭事务(可选) 关闭Session |
当然还有另外一个方法getCurrentSession() 这个方法就是通过SessionContext来减少Session创建的。比如常用的ThreadLocalSessionContext:
Session current = existingSession( factory ); if (current == null) { current = buildOrObtainSession(); current.getTransaction().registerSynchronization( buildCleanupSynch() ); if ( needsWrapping( current ) ) { current = wrap( current ); } doBind( current, factory ); } return current;
currentSession()内部先从ThreadLocal中获取Session。若不为null直接返回,若为null则openSession(...)一个Session并把这个Session对象绑定在ThreadLocal上。它就是通过在ThreadLocal中注册绑定Session来确保线程中最多只有一个Session对象。
=====================================================
Spring提供许多Template对各种底层ORM等进行集成,如JdbcTemplate、HibernateTemplate、JpaTemplate等同时也提供了相应的Dao模板类,如JdbcDaoSupport、HibernateDaoSupport、JpaDaoSupport等
既然说Spring对Hibernate的集成,就得看HibernateTemplate和HibernateDaoSupport这两个类。
使用HibernateDaoSupport进行数据操作时常用两种方式访问Session:getSession()和HibernateCallback。
注意:使用HibernateDaoSupport时候、如果再通过SessionFactory进行getCurrentSession()获取Session的话就有可能出现 问题了。因为 Spring的Bean工厂把Hibernate的SessionFactory动了点小手脚 ~ . 就是前面说前面说到的ThreadLocalSessionContext被Spring替换为 SpringSessionContext !
这个SpringSessionContext的currentSession()方法核心如下: doGetSession()会通过 TransactionSynchronizationManager访问线程资源、但是整个操作中没有打开事务的话、 此方法会抛出异常: if (!allowCreate && !isSessionTransactional(session, sessionFactory)) { closeSession(session); throw new IllegalStateException("No Hibernate Session bound to thread, " + "and configuration does not allow creation of non-transactional one here"); } |
getSession和getCurrentSession实现过程差不多,但是精简了SessionContext那一段。并且可以设置allowCreate,如果设置为false,此方法应该和getCurrentSession同样效果。
了解了这些之后、我进行了一个HibernateTemplate和getSession之间的测试,主要就是测试
单线程下多次重复请求创建Session与事务之间的关系、HibernateTemplate和getSession()两者之间在功能实现和效率上有什么样的不同。
Dao层: | testTS()方法——临时创建用于测试的方法 通过HibernateTemplate和直接getSession等方法获取Session并保存 打印已经保存的Session的状态 打印已经保存的Session之间是否相同 打印SessionFactory的状态记录 |
Service层: | testTS()方法——临时创建用于测试的方法 内部代码为执行三次请求: getDao().testTS(); getDao().testTS(); getDao().testTS(); |
第一次测试
Service层的testTS(e)方法不打开事务 使用HibernateTemplate
|
结论: Service层没有打开事务,但是每次操作CURD操作时,HibernateTemplate都会自动创建 Transactional。同一线程获取到的前后两个Session之间互不相同。 |
--------------Closed Transactional && Single-Thread--------------
测试数据扩大100倍并记时: |
第二次测试
Service层的testTS(e)方法打开事务
|
结论: 在事务边界内获取的Session由Spring管理、不必手动关闭 虽然打开了事务,但是同一线程下同一事务边界内前后获取的两个Session仍然不同! 这是为什么??? 后面会继续剖析HibernateTemplate源码,给出解释
|
--------------Open Transactional && Single-Thread--------------
测试数据扩大100倍并记时: |
第三次测试
Service层的testTS(e)方法不打开事务
|
可以看到getSession()由于缺少Spring的支持,在无事务环境下。 它为每个CURD请求创建了一个Session。并且更让人震惊的是,它好像为每个Session都创建了新 Connection 。这些Session使用之后,它也并不关闭。 |
--------------Closed Transactional && Single-Thread--------------
测试数据扩大100倍并记时,令人极其震惊且极其坑爹的一幕出现了!我亲眼看到程序以一种 肉眼可见的速度 慢腾腾的执行一次又一次的循环,它竟然执行了一分钟!!!: |
第四次测试
Service层的testTS(e)方法打开事务 使用getSession()
|
这个最容易让人理解、由于同一线程且在同一事务边界内,前后两个Session相同。并且也不会重复创建 Connection。 |
--------------Open Transactional && Single-Thread-------------- 测试数据扩大100倍并记时: |
总结:
getSession()在事务边界内会通过TransactionSynchronizationManager获取Session资源,同一线程内它不会重复创建Connection , 那些获取到的Session()不需要手动关闭。但是在无声明式事务环境下,它就会表现出极其坑爹的状况,它反复获取Connection,也不关闭Session。非常消耗资源
HibernateTemplate有一个安全且高效的Session环境,它的CRUD都是位于HibernateCallback内部,如果它的CRUD并没有位于事务之中,它会自己创建一个事务(Spring集成Hibernate时,所有的资源都是由TransactionSynchronizationManager管理的 )。同一个线程中它只需要一个Connection。Session也会在事务边界处自动关闭,程序员不需要关注此事(这个事务边界可能是@Transactional显式声明的也可能是HibernateTemplate#doExecute隐式声明的)。
在同一个事务边界内,两个HibernateTemplate操作内部的Session互也不相同这个问题可以在HibernateTemplate源码中找到答案:
protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNewSession, boolean enforceNativeSession)
throws DataAccessException {
......
Session session = (enforceNewSession ?
SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
boolean existingTransaction = (!enforceNewSession &&
(!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Session sessionToExpose =
(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session)
);
T result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
......
}
红色部分可以看到HibernateTemplate中获取的Session不是原生的,而是代理的。那个代理类是一个比较简单的内部类,源代码位于HibernateTemplate类文件最下部分。
每次HibernateTemplate#doExecute执行时除非声明不使用代理类,Spring都会使用线程中的Session资源来创建代理。但是这个代理类的创建对性能的影响微不足道了,Spring这样做肯定有它的道理。
各位英雄好汉,不要乱投新手帖、隐藏贴奥。
咱琢磨了好几天的东西,你们高手们就不要否决了