Mybatis源码系列3-三种SqlSession的区别(下)

简介: Mybatis源码系列3-三种SqlSession的区别(下)
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();
    }
  }


总结


  • DefaultSqlSessionSqlSessionManager 是Mybatis 默认提供的两个sqlsesion;SqlSessionTemplate是Mybatis与Spring整合时用的sqlsesion
  • DefaultSqlSession 是单例线程不安全的,SqlSessionManager与SqlSessionTemplate 是单例线程安全的
  • SqlSessionManagerSqlSessionTemplate 都对通过动态代理技术对DefaultSqlSession 自动关闭问题进行了优化
  • SqlSessionManager是自己管理sqlsession,SqlSessionTemplate外包给TransactionSynchronizationManager管理sqlsession。

如果本文任何错误,请批评指教,不胜感激 !


相关文章
|
21天前
|
SQL Java 数据库连接
hibernate和mybatis的区别
hibernate和mybatis的区别
|
21天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(User.java)
mybatis简单案例源码详细【注释全面】——实体层(User.java)
16 0
|
21天前
|
SQL Java 数据库连接
MyBatis #与$的区别以及动态SQL
MyBatis #与$的区别以及动态SQL
17 0
|
21天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
21天前
|
SQL 缓存 Java
|
21天前
|
Java 数据库连接 mybatis
MyBatis中Mapper接口和dao区别是什么?
MyBatis中Mapper接口和dao区别是什么?
|
21天前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
27 1
|
21天前
|
SQL Java 数据库连接
MyBatis之魂:探索核心接口SqlSession的神秘力量
MyBatis之魂:探索核心接口SqlSession的神秘力量
33 3
MyBatis之魂:探索核心接口SqlSession的神秘力量
|
21天前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
42 1
深入源码:解密MyBatis数据源设计的精妙机制
|
21天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
14 0