MyBatis(六)SqlSessionTemplate是如何保证线程安全的

前面说到DefaultSqlSession不是线程安全的,所以在MyBatis和spring项目整合的时候不能直接使用DefaultSqlSession,而是自己封装了一个线程安全的SqlSessionTemplate,通过spring管理的SqlSessionTemplate来创建SqlSession,而SqlSessionTemplate又是单例的,那么它是怎么保证线程安全的呢?

SqlSessionTemplate

最容易想到的就是ThreadLocal,在每个线程保存各自的SqlSession,这样执行commit,close操作就不需要担心线程安全的问题了。
那么就看看spring里到底是怎么实现的吧


    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
    }

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        Assert.notNull(executorType, "Property 'executorType' is required");
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        //代理模式,使用SqlSessionInterceptor来增强
        this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
    }

这里的核心处理就在增强的SqlSessionInterceptor的invoke方法里

private class SqlSessionInterceptor implements InvocationHandler {
        private SqlSessionInterceptor() {
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {           
            //通过一个工具类获取SqlSession(参数有SqlSessionFactory,Executor类型等)
            SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

            Object unwrapped;
            try {
                Object result = method.invoke(sqlSession, args);
                //判断当前SqlSession是否是Spring管理的,如果不是,就直接调用SqlSession的commit方法
                if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }

                unwrapped = result;
            } catch (Throwable var11) {
                unwrapped = ExceptionUtil.unwrapThrowable(var11);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    //如果出现异常,则调用SqlSessionUtils的closeSqlSession方法,这里执行后finally就不会再执行closeSqlSession了 因为sqlSession 被置为null了
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    sqlSession = null;
                    Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                    if (translated != null) {
                        unwrapped = translated;
                    }
                }

                throw (Throwable)unwrapped;
            } finally {
                if (sqlSession != null) {
                    //最后都会调用一次closeSqlSession来释放资源
                    SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                }

            }

            return unwrapped;
        }
    }

不难看出,保证线程安全的关键代码在SqlSessionUtils里,最重要的是其中的getSqlSession方法

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        Assert.notNull(executorType, "No ExecutorType specified");
        //根据SqlSessionFactory从TransactionSynchronizationManager中获取SqlSessionHolder,这里的SqlSession就是从TransactionSynchronizationManager的ThreadlLocal以sqlSessionFactory为key获取到的
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        //通过holder获取SqlSession,并且将其引用计数referenceCount + 1 
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
            return session;
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Creating a new SqlSession");
            }
            //如果holder里没有SqlSession,那么就通过SqlSessionFactory创建一个新的DefaultSqlSession
            session = sessionFactory.openSession(executorType);
            //注册生成的SqlSession
            registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
            return session;
        }
    }


---- TransactionSynchronizationManager 
    
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");


    public static Object getResource(Object key) {
        //根据sqlSessionFactory获取一个实际的key--如果是代理对象,actual就是目标Key对象
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Object value = doGetResource(actualKey);
        if (value != null && logger.isTraceEnabled()) {
            logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        }

        return value;
    }
    
    private static Object doGetResource(Object actualKey) {
        //从ThreadLocal里获取当前线程的map,然后从map里以sqlSessionFactory为key获取对应的value也就是SqlSessionHolder
        Map<Object, Object> map = (Map)resources.get();
        if (map == null) {
            return null;
        } else {
            Object value = map.get(actualKey);
            if (value instanceof ResourceHolder && ((ResourceHolder)value).isVoid()) {
                map.remove(actualKey);
                if (map.isEmpty()) {
                    resources.remove();
                }

                value = null;
            }

            return value;
        }
    }

从上面的源码不难猜出:
registerSessionHolder的方法其实就是将SqlSessionFactory和SqlSessionHolder以key-value的方式存储在TransactionSynchronizationManager 的ThreadLocal里,这样我们才能够从ThreadLocal里获取到对应的SqlSessionHolder

    private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            //获取对应的环境信息
            Environment environment = sessionFactory.getConfiguration().getEnvironment();
            if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
                }
                //通过session,executorType,exceptionTranslator创建SqlSessionHolder
                SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
                //将sessionFactory,holder以key-value的方式保存在TransactionSynchronizationManager的ThreadLocal里
                TransactionSynchronizationManager.bindResource(sessionFactory, holder);
                TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
                holder.setSynchronizedWithTransaction(true);
                holder.requested();
            } else {
                if (TransactionSynchronizationManager.getResource(environment.getDataSource()) != null) {
                    throw new TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
                }

                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
                }
            }
        } else if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
        }

    }

------TransactionSynchronizationManager 类源码

 public static void bindResource(Object key, Object value) throws IllegalStateException {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = (Map)resources.get();
        if (map == null) {
            map = new HashMap();
            resources.set(map);
        }
        //存储sqlSessionFactory和sqlSessionHolder到map中
        Object oldValue = ((Map)map).put(actualKey, value);
        if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) {
            oldValue = null;
        }

        if (oldValue != null) {
            throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        } else {
            if (logger.isTraceEnabled()) {
                logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
            }

        }
    }



closeSqlSession方法如下:先从TransactionSynchronizationManager中获取holder,然后判断当前SqlSession是否是从holder获取的,如果是说明session由spring管理的,那么直接调用holder.released(),将其引用计数-1(这跟重入锁的涉及有点像);如果不是则直接关闭session

  public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
        Assert.notNull(session, "No SqlSession specified");
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        if (holder != null && holder.getSqlSession() == session) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
            }
            //spring管理,则使用holder进行释放,这里只是减少了一下引用计数,这样后面在本线程中可以复用
            holder.released();
        } else {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
            }

            session.close();
        }

    }
    
    //判断session是否由spring管理
    public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
        Assert.notNull(session, "No SqlSession specified");
        Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
        SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
        return holder != null && holder.getSqlSession() == session;
    }

SqlSessionManager

SqlSession除了默认的实现类DefaultSqlSession,还有一个线程安全的实现SqlSessionManager

从其源码不难看出,其线程安全也是通过ThreadLocal实现的,SqlSessionManager中存储了SqlSessionFactory,通过其创建SqlSession的代理对象


//SqlSessionManager提供了很多重载的newInstance方法,通过SqlSessionFactoryBuilder会话工厂
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
//用于保存与当前线程绑定的SqlSession
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();

  private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
    //通过jdk动态代理创建代理SqlSession
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[]{SqlSession.class},
        new SqlSessionInterceptor());
  }

 public void startManagedSession() {
    //绑定当前线程创建的SqlSession到ThreadLocal里
    this.localSqlSession.set(openSession());
  }

 @Override
  public SqlSession openSession() {
    return sqlSessionFactory.openSession();
  }


private class SqlSessionInterceptor implements InvocationHandler {
    public SqlSessionInterceptor() {
        // Prevent Synthetic Access
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //获取当前线程绑定的SqlSession
      final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
      if (sqlSession != null) {
        try {
          //执行数据库操作
          return method.invoke(sqlSession, args);
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      } else {
        //如果localSqlSessio中没有SqlSession,即当前线程中没有创建SqlSession,则调用openSession(这里直接使用SqlSessionFactory来创建SqlSession)
        final SqlSession autoSqlSession = openSession();
        try {
          //使用新创建的SqlSession执行数据库操作
          final Object result = method.invoke(autoSqlSession, args);
          autoSqlSession.commit();
          return result;
        } catch (Throwable t) {
          autoSqlSession.rollback();
          throw ExceptionUtil.unwrapThrowable(t);
        } finally {
          autoSqlSession.close();
        }
      }
    }
  }

SqlSessionManager在使用的时候需要先调用startManagedSession来创建会话并存入ThreadLocal,如果没有开启的话那就直接使用SqlSessionFactory创建的SqlSession(此时会话不放入ThreadLocal,就无法保证线程安全饿了)

SqlSessionManager还实现了SqlSession的接口方法,比如:insert,update,select,会直接调用sqlSessionProxy代理对象中相应的方法。实际执行就会执行SqlSessionInterceptor里的invoke方法

总结:

  • DefaultSqlSession 不是线程安全的,不要使用单例模式

  • SqlSessionTemplate 和 SqlSessionManager 都持有 SqlSessionFactory,并且都是线程安全的,都通过ThreadLocal来实现SqlSession的生命周期管理

  • SqlSessionTemplate 由 Spring 事务管理器决定是否共用 SqlSession。事务内部共用 SqlSession,非事务不共用 SqlSession。SqlSessionManager 由使用者决定是否共用 SqlSession

发布了58 篇原创文章 · 获赞 19 · 访问量 7531

猜你喜欢

转载自blog.csdn.net/qq_35448165/article/details/104483689
今日推荐