Mybatis SqlSessionTemplate source code analysis

Original text: http://www.cnblogs.com/daxin/p/3544188.html


Mybatis SqlSessionTemplate source code analysis

When using Mybatis to integrate with Spring, we used the SqlSessionTemplate class.

 

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

 

Through the source code, we can see that  SqlSessionTemplate implements the SqlSession interface, which means that we can use SqlSessionTemplate to proxy the previous DefaultSqlSession to complete operations on the database, but the DefaultSqlSession class is not thread-safe, so this class cannot be set to singleton mode of.

If it is a conventional development mode, we can get one from SqlSessionFactory every time we use DefaultSqlSession. However, after integrating with Spring, Spring provides a globally unique SqlSessionTemplate example to complete the function of DefaultSqlSession. The problem is: whether multiple dao use one SqlSessionTemplate, or one dao uses one SqlSessionTemplate, SqlSessionTemplate corresponds to one sqlSession, when multiple dao use one SqlSessionTemplate When web threads call the same dao, they use the same SqlSessionTemplate, that is, the same SqlSession, so how does it ensure thread safety? Let's analyze it together.

(1) First, create a proxy class through the following code, which means to create an instance of the proxy class of SqlSessionFactory. The proxy class implements the SqlSession interface and defines a method interceptor. If the method defined in the proxy class instance that implements the SqlSession interface is called, the call will be Directed to the invoke method of SqlSessionInterceptor

 

copy code
 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   }
copy code

The core code is in the invoke method of SqlSessionInterceptor.

copy code
 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   }
copy code

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

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

SqlSessionUtils.closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)

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

copy code
 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   } 
copy code
copy code
 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   } 
copy code

In fact, through the above code, we can see that Mybatis uses the proxy mode in many places. This mode can be said to be a classic mode. In fact, it is not widely used in Mybatis. Spring things, AOP, connection pool technology and other technologies proxy technology is used. In the following article, we will analyze Spring's abstract transaction management mechanism.


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325881387&siteId=291194637