Mybatis SqlSessionTemplate 源码解析

Mybatis SqlSessionTemplate 源码解析web

在使用Mybatis与Spring集成的时候咱们用到了SqlSessionTemplate 这个类。spring

 

    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
          <constructor-arg index="0" ref="sqlSessionFactory" />
    </bean>

 

经过源码咱们何以看到 SqlSessionTemplate 实现了SqlSession接口,也就是说咱们可使用SqlSessionTemplate 来代理以往的DefailtSqlSession完成对数据库的操做,可是DefailtSqlSession这个类不是线程安全的,因此这个类不能够被设置成单例模式的。sql

若是是常规开发模式 咱们每次在使用DefailtSqlSession的时候都从SqlSessionFactory当中获取一个就能够了。可是与Spring集成之后,Spring提供了一个全局惟一的SqlSessionTemplate示例 来完成DefailtSqlSession的功能,问题就是:不管是多个dao使用一个SqlSessionTemplate,仍是一个dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession,当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让咱们一块儿来分析一下。数据库

(1)首先,经过以下代码建立代理类,表示建立SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,若是调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法缓存

 

 1   public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
 2       PersistenceExceptionTranslator exceptionTranslator) {
 3 
 4     notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
 5     notNull(executorType, "Property 'executorType' is required");
 6 
 7     this.sqlSessionFactory = sqlSessionFactory;
 8     this.executorType = executorType;
 9     this.exceptionTranslator = exceptionTranslator;
10     this.sqlSessionProxy = (SqlSession) newProxyInstance(
11         SqlSessionFactory.class.getClassLoader(),
12         new Class[] { SqlSession.class },
13         new SqlSessionInterceptor());
14   }

核心代码就在 SqlSessionInterceptor的invoke方法当中。安全

 1   private class SqlSessionInterceptor implements InvocationHandler {
 2     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3       //获取SqlSession(这个SqlSession才是真正使用的,它不是线程安全的)
 4       //这个方法能够根据Spring的事物上下文来获取事物范围内的sqlSession
 5       //一会咱们在分析这个方法
 6       final SqlSession sqlSession = getSqlSession(
 7           SqlSessionTemplate.this.sqlSessionFactory,
 8           SqlSessionTemplate.this.executorType,
 9           SqlSessionTemplate.this.exceptionTranslator);
10       try {
11         //调用真实SqlSession的方法
12         Object result = method.invoke(sqlSession, args);
13         //而后判断一下当前的sqlSession是否被Spring托管 若是未被Spring托管则自动commit
14         if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
15           // force commit even on non-dirty sessions because some databases require
16           // a commit/rollback before calling close()
17           sqlSession.commit(true);
18         }
19         //返回执行结果
20         return result;
21       } catch (Throwable t) {
22         //若是出现异常则根据状况转换后抛出
23         Throwable unwrapped = unwrapThrowable(t);
24         if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
25           Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
26           if (translated != null) {
27             unwrapped = translated;
28           }
29         }
30         throw unwrapped;
31       } finally {
32         //关闭sqlSession
33         //它会根据当前的sqlSession是否在Spring的事物上下文当中来执行具体的关闭动做
34         //若是sqlSession被Spring管理 则调用holder.released(); 使计数器-1
35         //不然才真正的关闭sqlSession
36         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
37       }
38     }
39   }

在上面的invoke方法当中使用了俩个工具方法 分别是session

SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)mybatis

SqlSessionUtils.closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)app

那么这个俩个方法又是如何与Spring的事物进行关联的呢?工具

 1 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {     
 2     //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory建立了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
 3     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 
 4  //若是holder不为空,且和当前事务同步 
 5     if (holder != null && holder.isSynchronizedWithTransaction()) { 
 6       //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,缘由就是同一个事务中同一个sqlSessionFactory建立的sqlSession会被重用 
 7       if (holder.getExecutorType() != executorType) { 
 8         throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction"); 
 9       } 
10       //增长该holder,也就是同一事务中同一个sqlSessionFactory建立的惟一sqlSession,其引用数增长,被使用的次数增长 
11       holder.requested(); 
12    //返回sqlSession 
13       return holder.getSqlSession(); 
14     } 
15  //若是找不到,则根据执行类型构造一个新的sqlSession 
16     SqlSession session = sessionFactory.openSession(executorType); 
17  //判断同步是否激活,只要SpringTX被激活,就是true 
18     if (isSynchronizationActive()) { 
19    //加载环境变量,判断注册的事务管理器是不是SpringManagedTransaction,也就是Spring管理事务 
20       Environment environment = sessionFactory.getConfiguration().getEnvironment(); 
21       if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { 
22   //若是是,则将sqlSession加载进事务管理的本地线程缓存中 
23         holder = new SqlSessionHolder(session, executorType, exceptionTranslator); 
24   //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中 
25         bindResource(sessionFactory, holder); 
26   //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 
27         registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); 
28         //设置当前holder和当前事务同步 
29   holder.setSynchronizedWithTransaction(true); 
30   //增长引用数 
31         holder.requested(); 
32       } else { 
33         if (getResource(environment.getDataSource()) == null) { 
34         } else { 
35           throw new TransientDataAccessResourceException( 
36               "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); 
37         } 
38       } 
39     } else { 
40     } 
41     return session; 
42   } 
 1 public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { 
 2  //其实下面就是判断session是否被Spring事务管理,若是管理就会获得holder  
 3     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 
 4     if ((holder != null) && (holder.getSqlSession() == session)) { 
 5    //这里释放的做用,不是关闭,只是减小一下引用数,由于后面可能会被复用 
 6       holder.released(); 
 7     } else { 
 8    //若是不是被spring管理,那么就不会被Spring去关闭回收,就须要本身close 
 9       session.close(); 
10     } 
11   } 

其实经过上面的代码咱们能够看出 Mybatis在不少地方都用到了代理模式,这个模式能够说是一种经典模式,其实不牢牢在Mybatis当中使用普遍,Spring的事物,AOP ,链接池技术 等技术都使用了代理技术。在后面的文章中咱们来分析Spring的抽象事物管理机制。

相关文章
相关标签/搜索