MyBatis update、insert、delete方法调用 - litter-fish/ReadSource GitHub Wiki

获取 SqlSessionFactory 时序图

创建 SqlSessionFactory 对象

// SqlSessionFactoryBuilder
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
        // 通过XMLConfigBuilder解析配置文件,解析的配置相关信息都会封装为一个Configuration对象
        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
        // 创建DefaultSessionFactory对象
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            reader.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}

获取SqlSession

// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
public SqlSession openSession() {
    // 使用默认的执行器类型(默认是SIMPLE),默认隔离级别,非自动提交 委托给openSessionFromDataSource方法
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

获取session通过数据源

获取session通过数据源时序图:

// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        // 通过Confuguration对象去获取Mybatis相关配置信息, Environment对象包含了数据源和事务的配置
        final Environment environment = configuration.getEnvironment();

        // 获取事务管理器, 支持从数据源或者直接获取
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

        // 从数据源创建一个事务, 同样,数据源必须配置, mybatis内置了JNDI、POOLED、UNPOOLED三种类型的数据源,
        // 其中POOLED对应的实现为org.apache.ibatis.datasource.pooled.PooledDataSource,它是mybatis自带实现的一个同步、
        // 线程安全的数据库连接池 一般在生产中,我们会使用dbcp或者druid连接池
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 根据事务和执行器类型创建执行器实例
        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();
    }
}

获取事务管理器

// org/apache/ibatis/session/defaults/DefaultSqlSessionFactory.java
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {

    // 如果没有配置environment或者environment的事务管理器为空,则使用受管的事务管理器
    // 除非什么都没有配置,否则在mybatis-config里面,至少要配置一个environment,此时事务工厂不允许为空
    // 对于jdbc类型的事务管理器,则返回JdbcTransactionFactory,其内部操作mybatis的JdbcTransaction实现(采用了Facade模式),后者对jdbc连接操作
    if (environment == null || environment.getTransactionFactory() == null) {
        return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
}

使用事务管理器创建事务

// org/apache/ibatis/transaction/jdbc/JdbcTransactionFactory.java
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
}

创建执行器实例

// org/apache/ibatis/session/Configuration.java
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) { // 预处理语句(prepared statements)
        executor = new ReuseExecutor(this, transaction);
    } else {
        executor = new SimpleExecutor(this, transaction);
    }
    // 如果启用了全局的缓存(一级缓存),则使用缓存执行器。
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    // 植入插件
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}

Executor 的设计模式

SQL语句的执行

方式一 SqlSession.getMapper实现

获取getMapper的时序图:

public class DefaultSqlSession implements SqlSession {
    public <T> T getMapper(Class<T> type) {
        return configuration.<T>getMapper(type, this);
    }
}

public class Configuration {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }
}

public class MapperRegistry {
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // knownMappers 由解析mapper文件时候放入
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
            // 调用 MapperProxyFactory 实例化 T
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }
}

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
    public Class<T> getMapperInterface() {
        return mapperInterface;
    }
    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
    }
    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 创建 DAO 接口的 Proxy, mapperInterface 代表我们自己定义的DAO 接口
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

sql的执行过程

sql的执行过程时序图:

getMapper 最终通过 MapperProxyFactory 创建代理对象,而 MapperProxy 实现了InvocationHandler接口,因此当调用Proxy.newProxyInstance方法创建代理是会调用 MapperProxy 的 invoke 方法。

// org/apache/ibatis/binding/MapperProxy.java
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 如果方法是Object类的方法,则直接反射执行
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 获取 MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 执行sql语句
    return mapperMethod.execute(sqlSession, args);
}

首先获取 MapperMethod,接着执行sql语句

获取 MapperMethod

// org/apache/ibatis/binding/MapperProxy.java
private MapperMethod cachedMapperMethod(Method method) {
    // 根据方法从缓存中获取
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
        // 不存在则创建一个MapperMethod
        mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
        // 放入缓存
        methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
}

创建 MapperMethod 对象

// org/apache/ibatis/binding/MapperMethod.java
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // SqlCommand一个内部类 封装了SQL标签的类型 insert update delete select
    this.command = new SqlCommand(config, mapperInterface, method);
    // MethodSignature一个内部类 封装了方法的参数信息 返回类型信息等
    this.method = new MethodSignature(config, mapperInterface, method);
}

创建 SqlCommand 对象,封装了具体执行的动作

// org/apache/ibatis/binding/MapperMethod.SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
    final String methodName = method.getName();
    final Class<?> declaringClass = method.getDeclaringClass();
    // 解析 MappedStatement
    MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
            configuration);
    // 检测当前方法是否有对应的 MappedStatement
    if (ms == null) {
        // 检测当前方法是否有 @Flush 注解
        if (method.getAnnotation(Flush.class) != null) {
            // 设置 name 和 type
            name = null;
            type = SqlCommandType.FLUSH;
        } else {
            /*
             * 若 ms == null 且方法无 @Flush 注解,此时抛出异常。
             */
            throw new BindingException("Invalid bound statement (not found): "
                    + mapperInterface.getName() + "." + methodName);
        }
    } else {
        // 设置 name 和 type 变量
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
            throw new BindingException("Unknown execution method for: " + name);
        }
    }
}

创建 MethodSignature 对象, 封装方法签名,封装了接口当中方法的 参数类型 返回值类型 等信息对象

// org/apache/ibatis/binding/MapperMethod.MethodSignature
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
    // 通过反射解析方法返回类型
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
    if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
    } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
    } else {
        this.returnType = method.getReturnType();
    }
    // 检测返回值类型是否是 void、集合或数组、Cursor、Map 等
    this.returnsVoid = void.class.equals(this.returnType);
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
    this.returnsCursor = Cursor.class.equals(this.returnType);
    // 解析 @MapKey 注解,获取注解内容
    this.mapKey = getMapKey(method);
    this.returnsMap = this.mapKey != null;
    /*
     * 获取 RowBounds 参数在参数列表中的位置,如果参数列表中
     * 包含多个 RowBounds 参数,此方法会抛出异常
     */
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    // 获取 ResultHandler 参数在参数列表中的位置
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    // 解析参数列表
    this.paramNameResolver = new ParamNameResolver(configuration, method);
}

创建解析参数对象

// org/apache/ibatis/reflection/ParamNameResolver.java
public ParamNameResolver(Configuration config, Method method) {
    // 获取参数类型列表
    final Class<?>[] paramTypes = method.getParameterTypes();

    // 获取参数注解
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
        // 检测当前的参数类型是否为 RowBounds 或 ResultHandler
        if (isSpecialParameter(paramTypes[paramIndex])) {
            // skip special parameters
            continue;
        }
        String name = null;
        for (Annotation annotation : paramAnnotations[paramIndex]) {
            if (annotation instanceof Param) {
                hasParamAnnotation = true;
                // 获取 @Param 注解内容
                name = ((Param) annotation).value();
                break;
            }
        }
        // name 为空,表明未给参数配置 @Param 注解
        if (name == null) {
            // @Param was not specified.
            // 检测是否设置了 useActualParamName 全局配置
            if (config.isUseActualParamName()) {
                /*
                 * 通过反射获取参数名称。此种方式要求 JDK 版本为 1.8+,
                 * 且要求编译时加入 -parameters 参数,否则获取到的参数名
                 * 仍然是 arg1, arg2, ..., argN
                 */
                name = getActualParamName(method, paramIndex);
            }
            if (name == null) {
                // use the parameter index as the name ("0", "1", ...)
                // gcode issue #71
                /*
                 * 使用 map.size() 返回值作为名称,思考一下为什么不这样写:
                 *   name = String.valueOf(paramIndex);
                 * 因为如果参数列表中包含 RowBounds 或 ResultHandler,这两个参数
                 * 会被忽略掉,这样将导致名称不连续。
                 *
                 * 比如参数列表 (int p1, int p2, RowBounds rb, int p3)
                 *  - 期望得到名称列表为 ["0", "1", "2"]
                 *  - 实际得到名称列表为 ["0", "1", "3"]
                 */
                name = String.valueOf(map.size());
            }
        }
        // 存储 paramIndex 到 name 的映射
        map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
}

执行 execute 方法

public class MapperMethod {

  // 根据 SQL 类型执行相应的数据库操作
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result; // 返回结果
    switch (command.getType()) {
      // INSERT操作
      case INSERT: {
        // 对用户传入的参数进行转换
        Object param = method.convertArgsToSqlCommandParam(args);
        // 执行插入操作,rowCountResult 方法用于处理返回值
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        // 执行更新操作
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        // 执行删除操作
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        // 根据目标方法的返回类型进行相应的查询操作
        if (method.returnsVoid() && method.hasResultHandler()) {
          /*
           * 如果方法返回值为 void,但参数列表中包含 ResultHandler,表明使用者
           * 想通过 ResultHandler 的方式获取查询结果,而非通过返回值获取结果
           */
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          // 执行查询操作,并返回多个结果
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          // 执行查询操作,并将结果封装在 Map 中返回
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          // 执行查询操作,并返回一个 Cursor 对象
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          // 执行查询操作,并返回一个结果
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        // 执行刷新操作
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }

    // 如果方法的返回值为基本类型,而返回值却为 null,此种情况下应抛出异常
    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;
  }
}

传入参数转换

// org/apache/ibatis/binding/MapperMethod.java
public Object convertArgsToSqlCommandParam(Object[] args) {
    return paramNameResolver.getNamedParams(args);
}

委托paramNameResolver进程参数转换

// org/apache/ibatis/reflection/ParamNameResolver.java
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
        // 如果没参数
        return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
        /*
         * 如果方法参数列表无 @Param 注解,且仅有一个非特别参数,则返回该参数的值。
         * 比如如下方法:
         *     List findList(RowBounds rb, String name)
         * names 如下:
         *     names = {1 : "0"}
         * 此种情况下,返回 args[names.firstKey()],即 args[1] -> name
         */
        return args[names.firstKey()];
    } else {
        // 否则,返回一个ParamMap,修改参数名,参数名就是其位置
        final Map<String, Object> param = new ParamMap<Object>();
        int i = 0;
        for (Map.Entry<Integer, String> entry : names.entrySet()) {
            // 添加 <参数名, 参数值> 键值对到 param 中
            param.put(entry.getValue(), args[entry.getKey()]);
            // genericParamName = param + index。比如 param1, param2, ... paramN
            final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
            /*
             * 检测 names 中是否包含 genericParamName,什么情况下会包含?答案如下:
             *
             *   使用者显式将参数名称配置为 param1,即 @Param("param1")
             */
            if (!names.containsValue(genericParamName)) {
                // 添加 <param*, value> 到 param 中
                param.put(genericParamName, args[entry.getKey()]);
            }
            i++;
        }
        return param;
    }
}

调用DefaultSqlSession的 insert、update等

// 调用sqlSession的insert方法, 对返回值的类型进行了一些检查
result = rowCountResult(sqlSession.insert(command.getName(), param));

处理返回结果

// org/apache/ibatis/binding/MapperMethod.java
private Object rowCountResult(int rowCount) {
    final Object result;
    /*
     * 这里的 method 类型为 MethodSignature,即方法签名,包含了某个方法较为详细的信息。Cache
     * 某个方法指的是 Mapper 或 Dao 接口中的方法,比如上一节示例 AuthorDao 中的
     * insertMany 方法。
     */
    if (method.returnsVoid()) {
        // 方法返回类型为 void,则不用返回结果,这里将结果置空
        result = null;
    } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
        // 方法返回类型为 Integer 或 int,直接赋值返回即可
        result = rowCount;
    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
        // 如果返回值类型为 Long 或者 long,这里强转一下即可
        result = (long)rowCount;
    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
        // 方法返回类型为布尔类型,若 rowCount > 0,则返回 ture,否则返回 false
        result = rowCount > 0;
    } else {
        throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
    }
    return result;
}

方式二 SqlSession的 update、insert、delete 实现

方式一 SqlSession.getMapper实现最终都会调用方式二的逻辑:

update

update执行的时序图:

插入、更新以及删除操作最终都调用了 SqlSession 接口中的方法。这三个方法返回值均是受影响行数,是一个整型值

// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public int update(String statement, Object parameter) {
    try {
        dirty = true;
        // 获取 MappedStatement
        MappedStatement ms = configuration.getMappedStatement(statement);
        // 调用 Executor 的 update 方法
        return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

首先获取 MappedStatement,接着调用 Executor 的 update 方法

获取 MappedStatement

// org/apache/ibatis/session/Configuration.java
public MappedStatement getMappedStatement(String id) {
    return this.getMappedStatement(id, true);
}

public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
    if (validateIncompleteStatements) {
        buildAllStatements();
    }
    return mappedStatements.get(id);
}

调用 Executor 的 update 方法

public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    // 刷新二级缓存
    flushCacheIfRequired(ms);
    // 执行具体构建类的逻辑
    return delegate.update(ms, parameterObject);
}

首先刷新二级缓存,接着执行具体构建类的逻辑。

刷新二级缓存

// org/apache/ibatis/executor/CachingExecutor.java
private void flushCacheIfRequired(MappedStatement ms) {
    Cache cache = ms.getCache();
    if (cache != null && ms.isFlushCacheRequired()) {
        // 委托事务缓存管理器进行缓存清理
        tcm.clear(cache);
    }
}

委托事务缓存管理器进行缓存清理

// org/apache/ibatis/cache/TransactionalCacheManager.java
public void clear(Cache cache) {
    // 获取 TransactionalCache 对象,并调用该对象的 clear 方法
    getTransactionalCache(cache).clear();
}

首先获取 TransactionalCache,然后调用其clear 方法

获取 TransactionalCache

// org/apache/ibatis/cache/TransactionalCacheManager.java
private TransactionalCache getTransactionalCache(Cache cache) {
    // 从映射表中获取 TransactionalCache
    TransactionalCache txCache = transactionalCaches.get(cache);
    if (txCache == null) {
        // TransactionalCache 也是一种装饰类,为 Cache 增加事务功能
        txCache = new TransactionalCache(cache);
        transactionalCaches.put(cache, txCache);
    }
    return txCache;
}

事务缓存的一些重要属性

// TransactionalCache

private boolean clearOnCommit;
// 在事务被提交前,所有从数据库中查询的结果将缓存在此集合中
private final Map<Object, Object> entriesToAddOnCommit;
// 在事务被提交前,当缓存未命中时,CacheKey 将会被存储在此集合中
private final Set<Object> entriesMissedInCache;

清空缓存

// org/apache/ibatis/cache/decorators/TransactionalCache.java
public void clear() {
    clearOnCommit = true;
    // 清空 entriesToAddOnCommit,但不清空 delegate 缓存
    entriesToAddOnCommit.clear();
}

Executor 的 update 方法总结: 首先委托缓存装饰类TransactionalCache,清空缓存数据,接着执行具体构建类的update方法。

执行具体构建类的逻辑

// org/apache/ibatis/executor/BaseExecutor.java
public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    // 刷新一级缓存
    clearLocalCache();
    // 调用具体执行器实现的doUpdate方法
    return doUpdate(ms, parameter);
}

首先刷新一级缓存,接着调用具体执行器实现的doUpdate方法。

刷新一级缓存

// org/apache/ibatis/executor/BaseExecutor.java
public void clearLocalCache() {
    if (!closed) {
        // 清空 缓存查询结果
        localCache.clear();
        // 清空 存储过程类型的查询参数
        localOutputParameterCache.clear();
    }
}

上面的两个缓存都是通过PerpetualCache持有的

public class PerpetualCache implements Cache {
    private Map<Object, Object> cache = new HashMap<Object, Object>();
}

从上可知对于一级缓存直接使用了HashMap进行存储的。

接着看简单的执行器执行更新操作

// org/apache/ibatis/executor/ReuseExecutor.java
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    // 创建 StatementHandler
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    // 创建 Statement
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    // 调用 StatementHandler 的 update 方法
    return handler.update(stmt);
}

首先创建 StatementHandler,然后创建Statement,最后调用 StatementHandler 的 update 方法。

创建 StatementHandler

// org/apache/ibatis/session/Configuration.java
public StatementHandler newStatementHandler(Executor executor,
        MappedStatement mappedStatement, Object parameterObject,
        RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 创建具有路由功能的 StatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);

    // 应用插件到 StatementHandler 上
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
}

StatementHandler的适配器设计模式:

创建RoutingStatementHandler对象适配器类

// org/apache/ibatis/executor/statement/RoutingStatementHandler.java
public RoutingStatementHandler(Executor executor, MappedStatement ms,
        Object parameter, RowBounds rowBounds,
        ResultHandler resultHandler, BoundSql boundSql) {

    // 根据 StatementType 创建不同的 StatementHandler
    switch (ms.getStatementType()) {
        case STATEMENT:
            // 简单SQL的处理
            delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case PREPARED:
            // 预编译SQL的处理
            delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        case CALLABLE:
            // 存储过程的处理
            delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
            break;
        default:
            throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
}

为StatementHandler加入插件的执行逻辑

// org/apache/ibatis/plugin/InterceptorChain.java
public Object pluginAll(Object target) {
    // 遍历拦截器集合
    for (Interceptor interceptor : interceptors) {
        // 调用拦截器的 plugin 方法植入相应的插件逻辑
        target = interceptor.plugin(target);
    }
    return target;
}

创建 Statement

对于 ReuseExecutor 可以通过缓存Statement以便再次使用。

// org/apache/ibatis/executor/ReuseExecutor.java
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    // 先取缓存
    if (hasStatementFor(sql)) {
        stmt = getStatement(sql);
        applyTransactionTimeout(stmt);
    } else {
        // 获取JDBC连接
        Connection connection = getConnection(statementLog);
        // 调用语句处理器的prepare方法
        stmt = handler.prepare(connection, transaction.getTimeout());
        // 缓存 Statement
        putStatement(sql, stmt);
    }
    // 为 Statement 设置 IN 参数
    handler.parameterize(stmt);
    return stmt;
}

首先获取JDBC连接、接着调用语句处理器的prepare方法、最后为 Statement 设置 IN 参数。

获取JDBC连接

// org/apache/ibatis/executor/BaseExecutor.java
protected Connection getConnection(Log statementLog) throws SQLException {
    // 通过jdbc获取数据库连接
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
        return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
        return connection;
    }
}

通过jdbc获取数据库连接

// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public Connection getConnection() throws SQLException {
    if (connection == null) {
        openConnection();
    }
    return connection;
}
protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
        log.debug("Opening JDBC Connection");
    }
    // 通过连接池获取连接
    connection = dataSource.getConnection();
    if (level != null) {
        // 设置隔离级别
        connection.setTransactionIsolation(level.getLevel());
    }
    // 设置是否自动提交
    setDesiredAutoCommit(autoCommmit);
}

调用语句处理器的prepare方法

// org/apache/ibatis/executor/statement/BaseStatementHandler.java
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
        // 首先实例化语句,因为PREPARE和非PREPARE不同,所以留给具体子类实现
        statement = instantiateStatement(connection);

        // 设置语句超时时间
        setStatementTimeout(statement, transactionTimeout);

        // 设置fetch大小
        setFetchSize(statement);
        return statement;
    } catch (SQLException e) {
        closeStatement(statement);
        throw e;
    } catch (Exception e) {
        closeStatement(statement);
        throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
}

首先实例化Statement,接着设置语句超时时间与fetch大小。

实例化Statement

// org/apache/ibatis/executor/statement/SimpleStatementHandler.java
protected Statement instantiateStatement(Connection connection) throws SQLException {
    // 如果存在resultSetType设置
    if (mappedStatement.getResultSetType() != null) {
        return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
        return connection.createStatement();
    }
}

设置Statement超时时间

// org/apache/ibatis/executor/statement/BaseStatementHandler.java
protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {
    Integer queryTimeout = null;
    // 获取mapper语句设置的默认超时时间
    if (mappedStatement.getTimeout() != null) {
        queryTimeout = mappedStatement.getTimeout();
        // 如果mapper语句未设置则尝试从configuration配置中获取
    } else if (configuration.getDefaultStatementTimeout() != null) {
        queryTimeout = configuration.getDefaultStatementTimeout();
    }
    // 设置超时时间
    if (queryTimeout != null) {
        stmt.setQueryTimeout(queryTimeout);
    }
    // 处理事务中设置的超时时间
    StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);
}

处理事务中设置的超时时间

// org/apache/ibatis/executor/statement/StatementUtil.java
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout) throws SQLException {
    // 未设置事务超时时间,直接return
    if (transactionTimeout == null){
        return;
    }
    Integer timeToLiveOfQuery = null;
    // 如果未设置查询超时时间,则取事务实例中设置的超时时间
    if (queryTimeout == null || queryTimeout == 0) {
        timeToLiveOfQuery = transactionTimeout;
    // 如果事务实例中设置的超时时间 小于 查询设置的超时时间则以 事务实例中设置的超时时间的时间为准。
    } else if (transactionTimeout < queryTimeout) {
        timeToLiveOfQuery = transactionTimeout;
    }
    if (timeToLiveOfQuery != null) {
        // 重新设置statement超时时间
        statement.setQueryTimeout(timeToLiveOfQuery);
    }
}

设置fetch大小

protected void setFetchSize(Statement stmt) throws SQLException {
    // 同样先取mapper中设置的fetchSize,如果设置了就以此为值进行设置
    Integer fetchSize = mappedStatement.getFetchSize();
    if (fetchSize != null) {
        stmt.setFetchSize(fetchSize);
        return;
    }
    // 如果mapper中未进行设置,这尝试从configuration中取默认的fetchSize
    Integer defaultFetchSize = configuration.getDefaultFetchSize();
    if (defaultFetchSize != null) {
        stmt.setFetchSize(defaultFetchSize);
    }
}

为 Statement 设置 IN 参数

对于SimpleStatementHandler parameterize 方法是一个空实现, 查看PreparedStatementHandler的实现

// org/apache/ibatis/executor/statement/PreparedStatementHandler.java
public void parameterize(Statement statement) throws SQLException {
    // 通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中
    parameterHandler.setParameters((PreparedStatement) statement);
}

通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中

// org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java
public void setParameters(PreparedStatement ps) {
    /*
     * 从 BoundSql 中获取 ParameterMapping 列表,每个 ParameterMapping
     * 与原始 SQL 中的 #{xxx} 占位符一一对应
     */
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);

            // 检测参数类型,排除掉 mode 为 OUT 类型的 parameterMapping
            if (parameterMapping.getMode() != ParameterMode.OUT) {
                Object value;
                // 获取属性名
                String propertyName = parameterMapping.getProperty();

                // 检测 BoundSql 的 additionalParameters 是否包含 propertyName
                if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
                    value = boundSql.getAdditionalParameter(propertyName);
                } else if (parameterObject == null) {
                    value = null;
                    // 检测运行时参数是否有相应的类型解析器
                } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    /*
                     * 若运行时参数的类型有相应的类型处理器 TypeHandler,则将
                     * parameterObject 设为当前属性的值。
                     */
                    value = parameterObject;
                } else {
                    // 为用户传入的参数 parameterObject 创建元信息对象
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    // 从用户传入的参数中获取 propertyName 对应的值
                    value = metaObject.getValue(propertyName);
                }

                /////// 以上代码用于获取 #{xxx} 占位符属性所对应的运行时参数

                TypeHandler typeHandler = parameterMapping.getTypeHandler();
                JdbcType jdbcType = parameterMapping.getJdbcType();
                if (value == null && jdbcType == null) {
                    // 此处 jdbcType = JdbcType.OTHER
                    jdbcType = configuration.getJdbcTypeForNull();
                }

                /////// 以上取 #{xxx} 占位符属性对应的 TypeHandler
                try {

                    // jdbc下标从1开始,由具体的类型处理器进行参数的设置, 对于每个jdbcType, mybatis都提供了一个对应的Handler,
                    // 具体可参考上文TypeHandler详解, 其内部调用的是PrepareStatement.setXXX进行设置。
                    // 由类型处理器 typeHandler 向 ParameterHandler 设置参数
                    ////// 通过 TypeHandler 将运行时参数值设置到 PreparedStatement 中
                    typeHandler.setParameter(ps, i + 1, value, jdbcType);
                } catch (TypeException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                } catch (SQLException e) {
                    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
                }
            }
        }
    }
}

首先获取 #{xxx} 占位符属性所对应的运行时参数, 接着获取 #{xxx} 占位符属性对应的 TypeHandler, 然后通过 TypeHandler 将运行时参数值设置到 PreparedStatement 中。

经过前面的准备操作,可以调用 StatementHandler 的 update 方法执行 SQL

// org/apache/ibatis/executor/statement/PreparedStatementHandler.java
public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行 SQL
    ps.execute();
    // 返回受影响行数
    int rows = ps.getUpdateCount();
    // 获取用户传入的参数值,参数值类型可能是普通的实体类,也可能是 Map
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    // 获取自增主键的值,并将值填入到参数对象中
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
}

KeyGenerator 的实现类

Jdbc3KeyGenerator 用于获取插入数据后的自增主键数值。 SelectKeyGenerator 某些数据库不支持自增主键,需要手动填写主键字段,此时需要借助此获取主键值。 NoKeyGenerator,这是一个空实现

KeyGenerator接口

// before key generator 主要用于oracle等使用序列机制的ID生成方式
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

// after key generator 主要用于mysql等使用自增机制的ID生成方式
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

使用 Jdbc3KeyGenerator 获取自增主键

public class Jdbc3KeyGenerator implements KeyGenerator {

    public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();

    @Override
    public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        // do nothing
    }

    @Override
    public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
        processBatch(ms, stmt, getParameters(parameter));
    }

    public void processBatch(MappedStatement ms, Statement stmt, Collection<Object> parameters) {
        ResultSet rs = null;
        try {
            rs = stmt.getGeneratedKeys();
            final Configuration configuration = ms.getConfiguration();
            final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 获取主键字段
            final String[] keyProperties = ms.getKeyProperties();
            // 获取结果集 ResultSet 的元数据
            final ResultSetMetaData rsmd = rs.getMetaData();
            TypeHandler<?>[] typeHandlers = null;
            // ResultSet 中数据的列数要大于等于主键的数量
            if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
                // 遍历 parameters
                for (Object parameter : parameters) {
                    // there should be one row for each statement (also one for each parameter)
                    // 对于批量插入,ResultSet 会返回多行数据
                    if (!rs.next()) {
                        break;
                    }
                    final MetaObject metaParam = configuration.newMetaObject(parameter);
                    if (typeHandlers == null) {
                        // 为每个主键属性获取 TypeHandler
                        typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
                    }
                    // 填充结果到运行时参数中
                    populateKeys(rs, metaParam, keyProperties, typeHandlers);
                }
            }
        } catch (Exception e) {
            throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (Exception e) {
                    // ignore
                }
            }
        }
    }

    private Collection<Object> getParameters(Object parameter) {
        Collection<Object> parameters = null;
        if (parameter instanceof Collection) {
            parameters = (Collection) parameter;
        } else if (parameter instanceof Map) {
            /*
             * 如果 parameter 是 Map 类型,则从其中提取指定 key 对应的值。
             * 至于 Map 中为什么会出现 collection/list/array 等键。大家
             * 可以参考 DefaultSqlSession 的 wrapCollection 方法
             */
            Map parameterMap = (Map) parameter;
            if (parameterMap.containsKey("collection")) {
                parameters = (Collection) parameterMap.get("collection");
            } else if (parameterMap.containsKey("list")) {
                parameters = (List) parameterMap.get("list");
            } else if (parameterMap.containsKey("array")) {
                parameters = Arrays.asList((Object[]) parameterMap.get("array"));
            }
        }
        if (parameters == null) {
            parameters = new ArrayList<Object>();
            // 将普通的对象添加到 parameters 中
            parameters.add(parameter);
        }
        return parameters;
    }

    private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam,
            String[] keyProperties, ResultSetMetaData rsmd) throws SQLException {
        TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
        for (int i = 0; i < keyProperties.length; i++) {
            if (metaParam.hasSetter(keyProperties[i])) {
                TypeHandler<?> th;
                try {
                    Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
                    th = typeHandlerRegistry.getTypeHandler(keyPropertyType, JdbcType.forCode(rsmd.getColumnType(i + 1)));
                } catch (BindingException e) {
                    th = null;
                }
                typeHandlers[i] = th;
            }
        }
        return typeHandlers;
    }

    private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
        // 遍历 keyProperties
        for (int i = 0; i < keyProperties.length; i++) {
            // 获取主键属性
            String property = keyProperties[i];
            TypeHandler<?> th = typeHandlers[i];
            if (th != null) {
                // 从 ResultSet 中获取某列的值
                Object value = th.getResult(rs, i + 1);
                // 设置结果值到运行时参数中
                metaParam.setValue(property, value);
            }
        }
    }
}

事务的提交与回滚

事务提交时序图:

事务提交

// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public void commit(boolean force) {
    try {
        executor.commit(isCommitOrRollbackRequired(force));
        dirty = false;
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

缓存的提交操作

// org/apache/ibatis/executor/CachingExecutor.java
public void commit(boolean required) throws SQLException {
    // 清除一级缓存,执行事务提交
    delegate.commit(required);
    // 将存放在TransactionCache中的数据对象提交到PerpetualCache中
    tcm.commit();
}

清除一级缓存,执行事务提交

// org/apache/ibatis/executor/BaseExecutor.java
public void commit(boolean required) throws SQLException {
    if (closed) {
        throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    // 清除本地缓存
    clearLocalCache();
    // 刷新Statements
    flushStatements();
    if (required) {
        // 提交事务
        transaction.commit();
    }
}

清除本地缓存

public void clearLocalCache() {
    if (!closed) {
        // 清空 缓存查询结果
        localCache.clear();
        // 清空 存储过程类型的查询参数
        localOutputParameterCache.clear();
    }
}

刷新Statements

// org/apache/ibatis/executor/BaseExecutor.java
public List<BatchResult> flushStatements() throws SQLException {
    return flushStatements(false);
}

public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    return doFlushStatements(isRollBack);
}

// org/apache/ibatis/executor/ReuseExecutor.java
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    for (Statement stmt : statementMap.values()) {
        // 关闭statement
        closeStatement(stmt);
    }
    // 清空statement 缓存
    statementMap.clear();
    return Collections.emptyList();
}

调用底层连接提交事务

// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
        if (log.isDebugEnabled()) {
            log.debug("Committing JDBC Connection [" + connection + "]");
        }
        connection.commit();
    }
}

事务提交后,将存放在TransactionCache中的数据对象提交到PerpetualCache中

// org/apache/ibatis/cache/TransactionalCacheManager.java
public void commit() {
    // 把涉及到的TransactionCache都进行处理:提交到二级缓存,并清空数据
    for (TransactionalCache txCache : transactionalCaches.values()) {
        txCache.commit();
    }
}

二级缓存的处理

// org/apache/ibatis/cache/decorators/TransactionalCache.java
public void commit() {
    // 根据 clearOnCommit 的值决定是否清空 delegate
    if (clearOnCommit) {
        // 清空二级缓存
        delegate.clear();
    }
    // 刷新未缓存的结果到 delegate 缓存中
    flushPendingEntries();
    // 重置 entriesToAddOnCommit 和 entriesMissedInCache
    reset();
}

刷新未缓存的结果到 delegate 缓存中

// org/apache/ibatis/cache/decorators/TransactionalCache.java
private void flushPendingEntries() {
    // 把之前存放到 entriesToAddOnCommit 中的数据提交到二级缓存中,具体的说是存放到PerpetualCache类的一个HashMap中
    for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
        // 将 entriesToAddOnCommit 中的内容转存到 delegate 中
        delegate.putObject(entry.getKey(), entry.getValue());
    }
    for (Object entry : entriesMissedInCache) {
        if (!entriesToAddOnCommit.containsKey(entry)) {
            // 存入空值
            delegate.putObject(entry, null);
        }
    }
}

重置属性

// org/apache/ibatis/cache/decorators/TransactionalCache.java
private void reset() {
    clearOnCommit = false;
    // 清空集合
    entriesToAddOnCommit.clear();
    entriesMissedInCache.clear();
}

回滚时序图:

回滚操作

// org/apache/ibatis/session/defaults/DefaultSqlSession.java
public void rollback(boolean force) {
    try {
        executor.rollback(isCommitOrRollbackRequired(force));
        dirty = false;
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);
    } finally {
        ErrorContext.instance().reset();
    }
}

缓存的回滚操作

// org/apache/ibatis/executor/CachingExecutor.java
public void rollback(boolean required) throws SQLException {
    try {
        // 清空一级缓存,执行事务回滚
        delegate.rollback(required);
    } finally {
        if (required) {
            tcm.rollback();
        }
    }
}

清空一级缓存,执行事务回滚

// org/apache/ibatis/executor/BaseExecutor.java
public void rollback(boolean required) throws SQLException {
    if (!closed) {
        try {
            // 清空一级缓存
            clearLocalCache();
            // 刷新Statement
            flushStatements(true);
        } finally {
            if (required) {
                // 执行事务回滚
                transaction.rollback();
            }
        }
    }
}

执行事务回滚

// org/apache/ibatis/transaction/jdbc/JdbcTransaction.java
public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
        if (log.isDebugEnabled()) {
            log.debug("Rolling back JDBC Connection [" + connection + "]");
        }
        connection.rollback();
    }
}
⚠️ **GitHub.com Fallback** ⚠️