天天看点

mybatis源码解析(2):mapper执行调用

本文是结合spring boot使用mybatis源码来解析mybatis源码,使用mybatis-spring-boot-starter.1.3.0、spring-boot-2.1.6.RELEASE

我们大致分为两篇来介绍。

第一篇介绍解析mapper xml文件,实例化相关bean;

第二篇介绍mapper执行调用。

目录

    • 1. 使用示例
    • 2. 源码解析
        • 1. MapperProxy
      • 初始化MapperMethod
        • 1. MapperMethod
        • 2. SqlCommand(构建sql命令信息)
        • 3. MethodSignature(解析方法对应的相关信息:入参,出参)
      • 执行MapperMethod
        • 1. MapperMethod
        • 2. SqlSessionTemplate
        • 3. SqlSessionTemplate$SqlSessionInterceptor
        • openSqlSession
          • 1. SqlSessionUtils(mybatis-spring-*.jar)
          • 2. 创建SqlSession(DefaultSqlsessionFactory)
          • 3. 创建Executor(configuration)(Executor缓存机制,缓存机制暂时先不详细说明)
          • 4. 拦截器链:InterceptorChain(插件机制,可以另外详细了解,此处点到为止。可以分析Plugin类,注册插件)
        • 执行SqlSession
          • 1. DefaultSqlSession
          • 2. 执行Executor(跟踪到SimpleExecutor继承了BaseExecutor)
    • 3. 总结

1. 使用示例

结合上一篇的示例一起。

  1. TestService
    @Component
    public class TestService {
    
        @Autowired
        private OrderMapper orderMapper;
    
        @PostConstruct
        public void test() {
            orderMapper.createOrder(new Order());
        }
    }
               
    上文我们介绍了实例化Mapper(MapperProxy),把这个Mapper注入到相关的@Autowired注解的字段上。也就是把MapperProxy注入到上面的orderMapper中

2. 源码解析

MapperProxy是一个代理类。所以调试,直接跳入MapperProxy.invoke方法。

1. MapperProxy

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      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);
      //执行MapperMethod
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
      //从缓存中获取MapperMethod
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
        // 缓存中没有就创建,把configuration传进去
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
           

初始化MapperMethod

1. MapperMethod

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
      //创建SqlCommand、MethodSignature继续看看
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }
           

2. SqlCommand(构建sql命令信息)

public static class SqlCommand {
    //构建sqlCommand主要是为了填充这两个属性
    private final String name;  //全路径名称
    private final SqlCommandType type;  //insert/update等sql命令类型
    
	public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
        //解析 MappedStatement ,我们看看此处,内部方法
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      //... 
    }

    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
        Class<?> declaringClass, Configuration configuration) {
        //拼接当前statementId
      String statementId = mapperInterface.getName() + "." + methodName;
        //从configuration获取之前加载解析的MappedStatement(第一篇讲过)
      if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
      } 
      //...
        
      return null;
    }
}
           

3. MethodSignature(解析方法对应的相关信息:入参,出参)

public static class MethodSignature {

    //返回是否数组、map、void、cursor
    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    //返回类型
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    //分页情况rowBounds
    private final Integer rowBoundsIndex;
    //参数情况(里边包含SortedMap,表示参数信息<参数顺序,参数名称>,并解析@Param注解信息)
    private final ParamNameResolver paramNameResolver;
    
}
           

执行MapperMethod

1. MapperMethod

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
      //根据类型执行
    switch (command.getType()) {
      case INSERT: {
          //根据之前在MethodSignature.ParamNameResolver.SortedMap中保存的参数的顺序,获取相关参数的类型
    	Object param = method.convertArgsToSqlCommandParam(args);
          //我们分析一下sqlSession.insert。此处的sqlsession就是我们上一篇说到的SqlSessionTemplate
        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()) {
          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);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        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;
  }
           

2. SqlSessionTemplate

//我们上一篇知道此处是创建了一个SqlSessionInterceptor拦截的DefaultSqlsession,所以会先执行SqlSessionInterceptor.invoke方法
  private final SqlSession sqlSessionProxy;

  @Override
  public int insert(String statement, Object parameter) {
    return this.sqlSessionProxy.insert(statement, parameter);
  }
           

3. SqlSessionTemplate$SqlSessionInterceptor

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //openSession。获取DefaultSqlSession
        SqlSession sqlSession = getSqlSession(
            SqlSessionTemplate.this.sqlSessionFactory,
            SqlSessionTemplate.this.executorType,
            SqlSessionTemplate.this.exceptionTranslator);
        try {
            //执行DefaultSqlSession方法,我们进去看看
            Object result = method.invoke(sqlSession, args);
            //如果没有事务(spring管理事务),则强制提交
            if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                sqlSession.commit(true);
            }
            return result;
        } catch (Throwable t) {
            throw unwrapped;
        } finally {
            if (sqlSession != null) {
                //关闭sqlSession,或者释放SqlSession
                closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }
        }
    }
}
           

openSqlSession

1. SqlSessionUtils(mybatis-spring-*.jar)
/**
*   从spring事务管理器中获取sqlsession,如果没有,就创建sqlsession,把sqlsession注册进去(主要是用spring管理事务要用,提交或回滚时,sqlSession提交或回滚自己内部的一些缓存)
**/
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
        return session;
    }
	//openSession。我们进去追踪DefaultSqlsessionFactory.openSession ->DefaultSqlsessionFactory.openSessionFromDataSource
    session = sessionFactory.openSession(executorType);
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    return session;
}
           
2. 创建SqlSession(DefaultSqlsessionFactory)
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
        //前面讲到SqlSessionFactoryBean创建了这些环境、事务
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //通过事务管理器创建Transaction
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //创建Executor
        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. 创建Executor(configuration)(Executor缓存机制,缓存机制暂时先不详细说明)
protected boolean cacheEnabled = true;

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //不同的ExecutorType创建不同的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);
    }
    //默认开启Executor缓存
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    //拦截器执行,这是一个拓展点,我们可以看看
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}
           
4. 拦截器链:InterceptorChain(插件机制,可以另外详细了解,此处点到为止。可以分析Plugin类,注册插件)
public class InterceptorChain {

    //Interceptor拦截器,mybatis的插件机制就是实现该接口
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();

    //执行拦截器,我们可以看到调用该方法的有四个地方
    /**
    * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
	* ParameterHandler (getParameterObject, setParameters)
	* ResultSetHandler (handleResultSets, handleOutputParameters)
	* StatementHandler (prepare, parameterize, batch, update, query)
    **/
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

    //添加拦截器
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}
           

执行SqlSession

1. DefaultSqlSession
//      //方法名称, 参数
  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
        //从configuration中获取之前解析的MappedStatement(包含了sql的信息)
      MappedStatement ms = configuration.getMappedStatement(statement);
        //执行executor
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           
2. 执行Executor(跟踪到SimpleExecutor继承了BaseExecutor)
@Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
        //创建StatementHandler(RoutingStatementHandler)
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
        //通过transaction获取/创建connection(基本的jdbc操作,事务超时时间),执行prepare操作
      stmt = prepareStatement(handler, ms.getStatementLog());
        //执行PreparedStatementHandler.update
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }
           

3. 总结

我们本文概述了一些执行Mapper的过程

  1. 执行Mapper时,调用MapperProxy,初始化MapperMethod。
  2. 执行MapperMethod,调用SqlSessionTemplate,执行拦截器SqlSessionTemplate$SqlSessionInterceptor。
  3. openSqlSession,创建SqlSession,创建Executor,注册拦截器(插件)。
  4. 执行SqlSession,执行插件拦截器,执行Executor,返回结果。
  5. closeSqlSession。