2.1解决线程安全问题。
解决线程安全问题,sqlSessionManager 还有另一个方式,那就是使用线程本地变量,不同于每次执行CURD操作都重新获取一个DefaultSqlSession 。 线程本地变量这种方式是一个线程内使用同一个请求,这就大大节省了创建DefaultSqlSession 的时间,并且是线程安全的。
使用:
sqlSessionManager.startManagedSession();//绑定Session到线程本地变量 sqlSessionManager.insert()
原理:
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>(); public void startManagedSession() { this.localSqlSession.set(openSession()); }
当startManagedSession()开始线程本地变量时,会从sqlSessionFactory获取一个session 放入到线程本地localSqlSession中,绑定到当前线程。
当我们执行sqlSessionManager.insert方法时,执行到增强器的invoke方法时,会从localSqlSession获取绑定到当前线程的sqlsession 。
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //从线程本地变量里获取 final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } }
小结:我们可以看出,SqlSessionManager通过动态代理技术+线程本地变量,升级了DefaultSqlSession的使用。
SqlSessionTemplate
SqlSessionTemplate 是Mybatis与Spring 整合时的线程安全sqlsession .
来看其构造方法
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }
会发现他和SqlSessionManager 类似,SqlSessionTemplate 也使用了JDK动态代理技术来实现。SqlSessionTemplate 也有一个内部类增强器SqlSessionInterceptor。
private class SqlSessionInterceptor implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取连接 SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { //执行sqlsession 目标方法 Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
我们也从自动关闭与线程安全两个角度来看看SqlSessionTemplate
1.解决线程安全问题
不同于SqlSessionManager 自己管理session的方式,SqlSessionTemplate 把session的管理外包出去了
SqlSessionTemplate 把获取sqlsession的工作交给了SqlSessionUtils去做了。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { //从事务同步器中获取Sqlsession。 SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Creating a new SqlSession"); } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
SqlSessionUtils 会先尝试从TransactionSynchronizationManager
事务同步器中获取sqlsesion,获取不到再从工厂内获取。
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);//从事务同步器中获取sqlsession SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }
TransactionSynchronizationManager
在spring源码系列11:事务代理对象的执行 一文提过。是spring事务实现原理的重要组件。
TransactionSynchronizationManager 本身也是一个线程本地变量管理器。
从这一点来看,他和SqlSessionManager 是一样的。只是管理的方式不同,一个自己管,一个外包
2.解决自动关闭问题
同SqlSessionManager一样,在执行完session后,也会帮助close.
不同的是,
- session 如果是由TransactionSynchronizationManager管理的,则只会更新引用计数器,让Spring在托管事务结束时调用close回调,关闭session。
- session不是由TransactionSynchronizationManager 管理的,则直接关闭session
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if ((holder != null) && (holder.getSqlSession() == session)) { //计数减1 holder.released(); } else { //关闭session session.close(); } }
总结
DefaultSqlSession
与SqlSessionManager
是Mybatis 默认提供的两个sqlsesion;SqlSessionTemplate
是Mybatis与Spring整合时用的sqlsesionDefaultSqlSession
是单例线程不安全的,SqlSessionManager与SqlSessionTemplate 是单例线程安全的SqlSessionManager
与SqlSessionTemplate
都对通过动态代理技术对DefaultSqlSession 自动关闭问题进行了优化SqlSessionManager
是自己管理sqlsession,SqlSessionTemplate
外包给TransactionSynchronizationManager
管理sqlsession。
如果本文任何错误,请批评指教,不胜感激 !