为什么Mybatis DefaultSqlSession不是线程安全的

为什么Mybatis DefaultSqlSession不是线程安全的

首先在DefaultSqlSession的源码中明确说了不是线程安全的:

/**
*
* The default implementation for {@link SqlSession}.
* Note that this class is not Thread-Safe.
*
* @author Clinton Begin
*/

我的理解主要是两方面:

  1. 首先由于JDBC的Connection对象本身不是线程安全的,而session中又只有一个connection,所以不是线程安全的
  2. 一级缓存
    由于一级缓存是session级别的,所以如果多个线程同时使用session,当线程A进行了插入操作未完成,但是此时线程B进行查询并缓存了数据,这是就出现了一级缓存与数据库数据不一致的问题。

对于Sessioin与Connection的关系,有如下代码过程追踪:

1、通过SessionFactory创建session

SqlSession sqlSession = sqlSessionFactory.openSession();

2、可以追踪代码到org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 这里创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 这里将事务传递给执行器Executor,这个是session执行数据库操作的核心
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

3、当session执行操作时是通过executor来进行的,继续追踪Executor,在SimpleExecutor中最终创建了Statement这个JDBC对象,而这个对象是要通过connection创建的。

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 这里创建statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

4、继续prepareStatement

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 看下面的方法
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }
  
  protected Connection getConnection(Log statementLog) throws SQLException {
    // 这里最终同通过创建Executor时传入的transcation进行了连接获取
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

5、继续看transcation的getConnection

  @Override
  public Connection getConnection() throws SQLException {
    // 这里只要有连接了就不重新打开连接了(从数据源中再次获取),说明只能有一个连接在一个org.apache.ibatis.transaction.Transaction中
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

至此可以看到一次SqlSession的执行最终是通过是通过事务获取了连接,而一个session中只能有一个事务,一个事务又只能有一个连接,所以一个session中只有一个connection

以上代码追踪证实由于Connection的线程不安全,所以SqlSession也是线程不安全的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 主题是Mybatis一级和二级缓存的应用及源码分析。希望在本场chat结束后,能够帮助读者朋友明白以下三点。...
    余平的余_余平的平阅读 1,355评论 0 12
  • # 前言 在java程序员的世界里,最熟悉的开源软件除了 Spring,Tomcat,还有谁呢?当然是 Mybat...
    莫那一鲁道阅读 3,326评论 3 11
  • 1. 简介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的...
    笨鸟慢飞阅读 5,622评论 0 4
  • 1、面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: -- 抽象:抽象是将一类对象的共同特征总结...
    ccc_74bd阅读 993评论 0 1
  • 呵一口气,像雾消散。 手脚麻木是我对冬天最好的说明。 出行时,不戴口罩脸会生疼。
    G无类阅读 288评论 0 1