美文网首页
为什么Mybatis DefaultSqlSession不是线程

为什么Mybatis DefaultSqlSession不是线程

作者: 没有花的雪 | 来源:发表于2019-05-14 22:40 被阅读0次

为什么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 DefaultSqlSession不是线程

      本文链接:https://www.haomeiwen.com/subject/xpuyaqtx.html