Mybatis执行流程简析

一、前言

日常工作中,我们用到mybatis的时候,都是写一个Mapper接口+xml文件/注解形式,然后就可以在业务层去调用我们在Mapper接口中定义的CRUD方法,很方便,但一直都没有去研究过执行逻辑,下面附一篇我自己研究的过程。

二、注入过程分析

平时我们在使用时,都是直接注解标在Mapper接口上,从而去注入一个实例,但我们是并没有实现过这个Mapper接口的,就很容易想到必然是有一个代理类(MapperProxy)来帮我们执行真正的过程,而且既然我们定义了接口,那大概率就是JDK动态代理了。

注入的过程也比较简单,在我们使用时会注明一个mapper扫描的包路径,然后在SqlSessionFactory初始化的过程中,会去解析每个mapper接口,并将其放在Configuration的MapperRegistry中,实际存放位置是在MapperRegistry中Map<Class<?>, MapperProxyFactory<?>> knownMappers。

接下来我们在使用mapper接口时,会从knownMappers中去获取到对应的MapperProxyFactory,从而去实例化真正的代理类,代码如下:

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //关键步骤,里面会使用JDK动态代理,创建一个MapperProxy
      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) {
    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);
  }

}

三、执行过程分析

综上来看,其实我们真正的方法是由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);
    }
    //缓存方法
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //真正的执行逻辑
    return mapperMethod.execute(sqlSession, args);
  }
/**
* 根据sql类型,执行不同的分支
* convertArgsToSqlCommandParam会转换传入的参数
*/
public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
    	Object param = method.convertArgsToSqlCommandParam(args);
        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:
        //如果是void方法,并且自定义了ResultMap等映射,则执行此逻辑
        if (method.returnsVoid() && method.hasResultHandler()) {
          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()) {
        //返参为游标
          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;
  }

上面的查询方法,都会走到下面这两行代码

//获取sql执行的映射过程对象,包含了参数、数据源、返参等
MappedStatement ms = configuration.getMappedStatement(statement);
//执行查询方法
executor.query(ms, wrapCollection(parameter), rowBounds, handler);

接着会走到执行器executor,主要有两类实现:

      • org.apache.ibatis.executor.BaseExecutor(基本执行器)
      • CachingExecutor(带缓存的执行器)
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取BoundSql对象,包含了解析动态SQl生成的sql语句以及参数映射的封装
    BoundSql boundSql = ms.getBoundSql(parameter);
    //生成缓存key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //查询
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
public BoundSql getBoundSql(Object parameterObject) {
	//调用sqlSource获取BoundSql,sqlSource默认为DynamicSqlSource类型(动态SQL)
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    //若参数映射为空,手动创建boundSql
    if (parameterMappings == null || parameterMappings.isEmpty()) {
      boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }

    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
      String rmId = pm.getResultMapId();
      if (rmId != null) {
        ResultMap rm = configuration.getResultMap(rmId);
        if (rm != null) {
          hasNestedResultMaps |= rm.hasNestedResultMaps();
        }
      }
    }

    return boundSql;
  }
  public BoundSql getBoundSql(Object parameterObject) {
	//获取上下文对象,并将传入的入参对象及数据源标识放入bindings
    DynamicContext context = new DynamicContext(configuration, parameterObject);
	//基于动态sql解析成的List<SqlNode> contents,遍历赋值参数
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

准备好BoundSql后,就该执行真正的查询了,主要链路如下

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值