mybatis源码分析02:执行sql语句

648 阅读12分钟

注:本系列源码分析基于mybatis 3.5.6,源码的gitee仓库仓库地址:funcy/mybatis.

本文是mybatis源码分析的第三篇,我们来分析sql语句的执行流程。

准备mybatis示例demo一文中,我们提供的测试主类如下:

public class Test01 {
  public static void main(String[] args) throws Exception {
    // 配置文件路径
    String resource = "org/apache/ibatis/demo/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(inputStream);
    try (SqlSession sqlSession = factory.openSession()) {
      // 获取 mapper,进行查询操作
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      List<User> users = userMapper.selectList(3L, 10);
      System.out.println(users);
    }
  }
}

解析配置文件一文中,我们主要分析了SqlSessionFactory的构建过程,也就是

SqlSessionFactory factory = builder.build(inputStream);

我们知道,得到的factory类型为DefaultSqlSessionFactory,DefaultSqlSessionFactory中有一个成员变量为configuration,其中保存的正是配置文件解析后的内容。

本文继续分析该demo后面的内容。

1. DefaultSqlSessionFactory#openSession()

我们先来看这一行:

SqlSession sqlSession = factory.openSession()

这里调用的是DefaultSqlSessionFactory#openSession()方法,返回的是SqlSession。这个SqlSession是个啥呢?它的注释如下:

The primary Java interface for working with MyBatis. Through this interface you can execute commands, get mappers and manage transactions.

使用MyBatis的主要Java接口。 通过此接口,您可以执行命令,获取映射器和管理事务。

从注释来看,SqlSession就是mybatis的执行入口了。

我们进入DefaultSqlSessionFactory#openSession()方法:

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

  /**
   * 从数据源得到一个 SqlSession
   */
  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);
      // 创建执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回 SqlSession
      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();
    }
  }

可以看到,最终是调用openSessionFromDataSource(...)方法进行处理的,这个方法会拿到数据源,生成事务处理器,再创建执行器,最后封装为DefaultSqlSession。关于mybatis的事务处理,在SqlSession中有对应的方法处理回滚提交,需要在sql执行前后手动调用,这里本文就不多做分析了,我们重点关注创建执行器,也就是以下代码:

final Executor executor = configuration.newExecutor(tx, execType);

执行器创建的方法为Configuration#newExecutor(Transaction, ExecutorType),代码如下:

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    // 批量执行器
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    // 重用执行器
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    // 简单执行器
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    // 缓存装饰
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 处理 plugin(插件)
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

该方法先是根据执行器的类型创建对应的执行器,然后判断是否开启缓存来决定要不要创建缓存执行器,之后再处理插件。

关于mybatis的执行器及mybatis插件相关内容,我们后面再分析。这里我们来看看cacheEnabled的设置。经过一系列的追踪,cacheEnabled 的值来自于 XMLConfigBuilder#parseConfiguration 方法,而这个方法正是解析mybatis配置文件的所在:

private void parseConfiguration(XNode root) {
    try {
      ...
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      ...
      settingsElement(settings);
    }
    ...
}

因此,cacheEnabled的配置来自于mybatis配置文件的settings节点。

得到执行器executor后,就是DefaultSqlSession的创建了,对应方法如下:

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

这个方法仅是做了一些赋值操作,就不多作分析了。

关于DefaultSqlSession有一点需要特别注意:

从注释来看,DefaultSqlSession并不是线程安全的,实际使用时可以考虑为每个线程都创建一个 DefaultSqlSession(可以结合ThreadLocal实现),而mybatis就为我们提供了一个这样的sqlSessionSqlSessionManager,我们下面会分析。

2. 获取XxxMapper

获取XxxMapper的方法为sqlSession.getMapper(UserMapper.class),也就是DefaultSqlSession#getMapper,一路往下跟,最终进入的方法为MapperRegistry#getMapper:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从 knownMappers 中获取 MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory 
            = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 核心:实例化操作
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

这个方法先是从knownMappers获取MapperProxyFactory,然后再调用mapperProxyFactory.newInstance(...)进行实例化。

关于knownMappers,在前面分析mapper.xml解析时有提到过,它是一个MapkeyMapper的类型,valueMapperProxyFactory对象。

knownMappers.get(type)中,传入的type就是UserMapper.class,得到的value就是UserMapper.class对应的MapperProxyFactory了:

我们来看看UserMapper.class是如何实例化的,进入MapperProxyFactory#newInstance(SqlSession)

  public T newInstance(SqlSession sqlSession) {
    // 得到 MapperProxy
    final MapperProxy<T> mapperProxy = new MapperProxy<>(
            sqlSession, mapperInterface, methodCache);
    // 继续调用方法进行实例化
    return newInstance(mapperProxy);
  }

  /**
   * 进行实例化操作
   */
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), 
            new Class[] { mapperInterface }, mapperProxy);
  }

实例化时,先是创建MapperProxy实例,然后调用Proxy.newProxyInstance(...)方法进行实例化操作。

MapperProxy是个啥呢?它的定义如下:

public class MapperProxy<T> implements InvocationHandler, Serializable {
  // sql操作类
  private final SqlSession sqlSession;
  // Mapper 接口
  private final Class<T> mapperInterface;

  ..

  /**
   * 来自于 InvocationHandler 的方法
   * 代理方法的调用入口
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ...
  }
  ...
}

MapperProxy实现了InvocationHandler,用以处理jdk动态代理的操作,Proxy.newProxyInstance(...)方法就是用来处理代理对象的生成的。这也就是说,动态代理只要有接口就可以完成代理操作(并不需要实现类)!这就是动态代理的妙用了,我们用个demo模拟下:

public class DynamicProxyTest {

  /**
   * 定义一个接口
   */
  interface MyInterface {
    String hello();
  }

  /**
   * 实现 InvocationHandler 接口,重写 invoke(...) 方法
   */
  public static class MyInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if(method.getName().equals("hello")) {
       return "hello world";
      }
      return null;
    }
  }

  public static void main(String[] args) {
    // 生成代理对象
    MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(
      MyInterface.class.getClassLoader(), new Class[] { MyInterface.class },
      new MyInvocationHandler());
    // 方法调用
    String result = myInterface.hello();
    System.out.println(result);
  }

}

以上代码中,MyInterface并没有实现类,但我们调用myInterface.hello()时,依然能得到结果,因为该方法的处理逻辑在MyInvocationHandler#invoke中指定了。

同样地,UserMapper虽然没有实现类,但其中方法的逻辑都在MapperProxy#invoke处理,我们调用UserMapper的方法,都是调用MapperProxy#invoke方法。想必你已经猜到了,MapperProxy#invoke最终调用的必定是xml指定的sql语句,关于这方面的处理,我们下一节再分析。

到了这里,UserMapper的实例就能拿到了,尽管它没有实现类,但通过动态代理生成了它的实例。

3. 处理查询操作

拿到UserMapper后,就开始执行查询操作了 ,对应的代码为:

List<User> users = userMapper.selectList(3L, 10);

debug模式下进入该方法,就到了MapperProxy#invoke

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        // 如果是 Object 类的方法,直接调用
        return method.invoke(this, args);
      } else {
        // 缓存调用  
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

在上一节中,在分析UserMapper的实例时,我们知道了 UserMapper 是动态代理对象,方法执行时,会调用InvocationHandler#invoke方法,对应到UserMapper实例时,就是MapperProxy#invoke,这也与debug得到的结果一致。

MapperProxy#invoke传入的参数如下:

method的定义类显然不是Object,因此会调用cachedInvoker(...)方法,也就是:

return cachedInvoker(method).invoke(proxy, method, args, sqlSession);

这行代码包含两个方法:cachedInvoker(...)invoke(...),我们逐一进行分析。

3.1 cachedInvoker(...):执行器的生成

我们先来看cachedInvoker(...)方法:

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      // 从缓存里获取
      MapperMethodInvoker invoker = methodCache.get(method);
      if (invoker != null) {
        return invoker;
      }

      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          // 接口的默认方法
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          // 普通方法在这里调用
          return new PlainMethodInvoker(
                new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

执行时,先从缓存中获取Invoker,如果不存在则生成对应的Invoker。生成 Invoker 时,如果是接口的默认方法(java8的语法,接口中的方法可以使用default修饰),则生成DefaultMethodInvoker,否则生成PlainMethodInvoker,这里我们重点关注PlainMethodInvoker的生成:

return new PlainMethodInvoker(
        new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));

这个方法分为两部分,我们先来看MapperMethod的生成,也就是new MapperMethod(...)

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  ...
}

这个类中有两个成员变量:commandmethod

  • command:存放的是sql的一些信息,其中的name属性存放的是"包名.类名.方法名"
  • method:方法的签名,主要存放方法的一些信息

再来看看new PlainMethodInvoker(...)的流程:

  private static class PlainMethodInvoker implements MapperMethodInvoker {
    // 成员变量,存放传入的mapperMethod
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
      super();
      this.mapperMethod = mapperMethod;
    } 
    ...
  }

构造方法就只是存放了传入的mapperMethod

3.2 MapperMethodInvoker#invoke:方法的调用

cachedInvoker(...)方法中,我们得到的Invoker类是PlainMethodInvoker,接着我们来看看它的invoke(...)方法:

@Override
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) 
        throws Throwable {
    return mapperMethod.execute(sqlSession, args);
}

继续进入MapperMethod#execute方法:

/**
 * 处理sql操作
 */
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        ...
        break;
      }
      case UPDATE: {
        ...
        break;
      }
      case DELETE: {
        ...
        break;
      }
      case SELECT:
        // 处理查询操作
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          // 返回多行记录
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        ...
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" 
          + method.getReturnType() + ").");
    }
    return result;
  }

从代码来看,MapperMethod#execute会处理sql的INSERTmapper.xml中的insert标签)、UPDATEmapper.xml中的update标签)、DELETEmapper.xml中的delete标签)、SELECTmapper.xml中的select标签)、FLUSH(针对BatchExecutor执行器,执行缓存的Statement)等操作,这里我们仅关注select操作,其他操作与这个操作类似。

我们直接进入SELECT操作处理多行记录返回的方法result = executeForMany(sqlSession, args)

  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      // 查询操作
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

我们继续跟进sqlSession.selectList方法,然后代码进入 DefaultSqlSession#selectList(String, Object, RowBounds) 方法:

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 调用执行器查询
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

这里传入的statementorg.apache.ibatis.demo.mapper.UserMapper.selectList,在前面解析mapper.xml文件时,select/update/insert/delete标签的内容都会被解析成MappedStatement对象,保存到Configuration#mappedStatements,这是一个Map,key就是Mapper.java接口的“包名.类名.方法名”,configuration.getMappedStatement(statement)就是获取org.apache.ibatis.demo.mapper.UserMapper.selectList对应的MappedStatement,得到的结果如下:

获取到MappedStatement后,接下来的操作就由执行器进行处理了,由于默认启用了一级缓存,因此最先执行的是缓存执行器的方法CachingExecutor#query(...)

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
        ResultHandler resultHandler) throws SQLException {
    // 得到要执行的 sql
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建缓存 key
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 继续执行 query 操作
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

这里得到的boundSql包含了要执行的sql语句,以及传入的参数:

继续跟进query(...)方法:

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
        ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    // 操作缓存
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
        // 从缓存中获取
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          // 实际处理查询的操作
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          // 添加到缓存中
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

可以看到查询时,先判断缓存中是否存在要查询的记录,如果存在则直接返回缓存中的记录,否则就调用delegate.query(...)进行查询。

delegate是啥呢?这个就是执行具体操作的执行器,CachingExecutor相当于一个装饰器,将具体的执行器包装了一层,以使其拥有缓存的功能。关于mybatis的执行器,我们会在下一篇文章中详细介绍。

由于是第一次调用,显然缓存中是没有记录的,我们继续跟进delegate.query(...)方法,最终进入的是SimpleExecutor#doQuery方法:

  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);
      // 得到 PrepareStatement
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行查询
      return handler.query(stmt, resultHandler);
    } finally {
      // 关闭 Statement
      closeStatement(stmt);
    }
  }

在这个方法中会处理获取数据库连接,获取Statement,执行查询等操作,这里我们仅关注查询操作,跟进handler.query(...)方法,最终进入了PreparedStatementHandler#query

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) 
        throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行
    ps.execute();
    // 处理返回结果
    return resultSetHandler.handleResultSets(ps);
  }

这个方法主要是执行PreparedStatement,然后处理返回结果,算是很常规的jdbc操作了。

4. 线程安全的sqlSessionSqlSessionManager

前面提到DefaultSqlSession是非线程安全的,而mybatis也提供了一个线程安全的sqlSessionSqlSessionManager,我们先来看看它的使用方式:

public class Test03 {
  public static void main(String[] args) throws Exception {
    // 配置文件路径
    String resource = "org/apache/ibatis/demo/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionManager factory = builder.build(inputStream);
    // 使用 SqlSessionManager
    SqlSessionManager sqlSessionManager
      = SqlSessionManager.newInstance(factory);
    // 获取 mapper,进行查询操作
    UserMapper userMapper = sqlSessionManager.getMapper(UserMapper.class);
    List<User> users = userMapper.selectList(3L, 10);
    System.out.println(users);
  }
}

对于下SqlSessionManagerDefaultSqlSessionFactory的使用差别:

从代码上看,SqlSessionManager承担了SqlSessionFactorySqlSession的功能。

SqlSessionManager是如何实现线程安全的呢?我们先看看它的构造方法:

public class SqlSessionManager implements SqlSessionFactory, SqlSession {

  /** 传入的 sqlSessionFactory */
  private final SqlSessionFactory sqlSessionFactory;

  /** 这就是SqlSession */
  private final SqlSession sqlSessionProxy;

  public static SqlSessionManager newInstance(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionManager(sqlSessionFactory);
  }

  private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
    this.sqlSessionFactory = sqlSessionFactory;
    // 动态代理,代理的类是 SqlSession
    this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[]{SqlSession.class},
        // 这就是动态代理的 InvocationHandler 了
        new SqlSessionInterceptor());
  }

  ...

} 

从以上代码来看,

  1. SqlSessionManager同时实现了SqlSessionFactorySqlSession两个接口,因此在DefaultSqlSession中需要获取SqlSesession的操作,在使用了SqlSessionManager就不需要了
  2. SqlSessionManager维护了一个成员变量sqlSessionFactory,这个变量由参数传入
  3. SqlSessionManager的另一成员变量为sqlSessionProxy,它在构造方法中由动态代理生成,其InvocationHandler的实现为SqlSessionInterceptor

sqlSessionProxySqlSession的代理,而SqlSession支持的方法如下:

也就是说,在执行SqlSession数据库相关操作时,会被sqlSessionProxy拦截到,比如我们执行的查询操作:

List<User> users = userMapper.selectList(3L, 10)

最终也会调用到SqlSession#selectList(...)方法。

我们直接进入SqlSessionInterceptor#invoke方法:

public class SqlSessionManager implements SqlSessionFactory, SqlSession {

  private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();

  ...

  private class SqlSessionInterceptor implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 从 ThreadLocal 中获取 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 {
        // 开启一个 SqlSession
        try (SqlSession autoSqlSession = openSession()) {
          try {
            // 处理方法执行
            final Object result = method.invoke(autoSqlSession, args);
            autoSqlSession.commit();
            return result;
          } catch (Throwable t) {
            autoSqlSession.rollback();
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
      }
    }
  }

  ...
}

SqlSessionInterceptor#invoke(...)方法来看,SqlSessionManager中维护了一个ThreadLocal<SqlSession>,当需要使用SqlSession时,就会先从该ThreadLocal中获取,ThreadLocal中不存在时才开启一个SqlSession,这样就保证了SqlSession为线程独占,从而使SqlSession线程安全了。

5. 总结

本文主要介绍了mybatis执行sql的流程,介绍的内容如下:

  1. mybatis的sql执行操作方法在SqlSession中,SqlSessionmybatis的执行入口
  2. XxxMapper是一个接口,mybatis基于jdk动态代理机制会生成一个代理对象,其InvocationHandler(具体类为MapperProxy)的invoker(...)方法会获取mapper.xml定义的sql并执行
  3. 执行XxxMapper方法时,实际调用的是MapperProxy#invoker(...)方法,整个方法的执行过程中,会获取mapper.xml中的sql语句,然后使用执行器(SimpleExecutorReuseExecutorBatchExecutor等)处理sql的执行
  4. mybatis提供的DefaultSqlSession是非线程安全的,想要线程安全,可以使用SqlSessionManager

本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。