How does spring manage mybatis (2) ----- Thread safety of SqlSession

  In the previous article, we learned that the final database operation is the method of the proxy class:

 @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 {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          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);
        }
      }
    }

我们可以看到每次都是使用getSqlSession()来获取真是sqlsession的,而获取的sqlSession又是DefaultSqlSession,这个类我们知道他是线程不安全的,之前使用都是采用多实例模式,就是每次使用都new一个,但是spring采用了更加聪明的方式可以使它不需要每次new一个也可以保持线程安全。

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    
  SqlSessionHolder holder
= (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); SqlSession session = sessionHolder(executorType, holder); if (session != null) { return session; } session = sessionFactory.openSession(executorType); registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }

We saw

TransactionSynchronizationManager.getResource(sessionFactory)

 This method is mainly used to obtain a session holder in the transaction management of the current thread.

sessionHolder(executorType, holder)

 This method, his role is to obtain a session resource and register it.

 If the session is not obtained, it will create one by itself and execute it

registerSessionHolder

   This method attempts to put the created session into the current thread context.

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
    SqlSessionHolder holder;
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();
      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        holder.setSynchronizedWithTransaction(true);
        holder.requested();
      } 
}

  Let's focus on this method, first

TransactionSynchronizationManager.isSynchronizationActive()
    public static boolean isSynchronizationActive() {
        return (synchronizations.get() != null);
    }

这个方法主要判断当前线程的synchronizations是不是有值的,那这个值得初始化在哪里呢,他的初始化发生在事务拦截器中,当创建一个事务时会为当前的线程添加synchronizations,当该事务结束时会将他清空。

  

        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        holder.setSynchronizedWithTransaction(true);
        holder.requested();

接下来的就是创建一个session的持有者,然后把他绑定到当前的事务管理中,这样只要是在该事务中的事务操作都可以使用这个sqlsession,因为他们一定是在同一线程,他们的动作一定是互斥的,这样可以保证线程的安全性。

  综上所述,我们可以总结下,当我们的程序被spring的事务管理时,程序中的数据库操作可以使用同一个SqlSession,而不会产生线程安全问题。

  

Guess you like

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