MyBatis原理-查询

源码分析

在dao调用查询数据库的方法上打上断点后,点击 step into 就会进入到动态代理生成的 MapperProxy 类中

java 复制代码
// MapperProxy.class 执行invoke 方法  
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 {
        // 真正执行
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
 
 
  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        // 是不是默认方法 接口可以有default方法
        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;
    }
  }
  
    // MapperMethod.class
    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
      this.command = new SqlCommand(config, mapperInterface, method);
      this.method = new MethodSignature(config, mapperInterface, method);
    }

创建 MapperMethod 的时候就已经在构造方法中对一些属性赋过值了。

然后通过上一步拿到的 MapperMethodInvoker 调用 invoke() :

java 复制代码
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

再通过 MapperMethod 调用 execute() 真正执行:

java 复制代码
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      // insert语句
      case INSERT: {
        // 参数解析器
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      // update语句
      case UPDATE: {
        // 参数解析器
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      // delete语句
      case DELETE: {
        // 参数解析器
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      // select语句
      case SELECT:
        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);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        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;
  }

1、上面这段代码就是通过判断你是要增删改查的哪一种,我们这里调用的查方法,所以会到查询里面

2、到查询里面的判断后,会再次判断你的返回值是返回集合、还是返回的是map、还是单个的或者是其它的类型

我这里接下来会调用 MapperMethod#executeForMany()

java 复制代码
  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    // 解析你传的参数 ParamNameResolver
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      // 逻辑分页
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      // command.getName()= 全类名+方法名 如:com.lizhi.dao.EmployeeDao.selectAll
      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;
  }

通过 SqlSessionTemplate 类,调用 DefaultSqlSession#selectList() 来查询数据

java 复制代码
public class DefaultSqlSession implements SqlSession {
 
 private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, 
 ResultHandler handler) {
    try {
      // 这里的statement就是刚传过来的command.getName()也就是全类名
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 通过执行器来查询(执行器也是四大对象之一)
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
 
  // 如果参数类型是集合的话,通过参数名字解析器包装成map
  // 集合:key=list,value=值     数组:key=array,value=值
  private Object wrapCollection(final Object object) {
    return ParamNameResolver.wrapToMapIfCollection(object, null);
  }
}  
 
// 所有的配置都在这个里面,这里面有很多的东西
public class Configuration {
      protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
          
        public MappedStatement getMappedStatement(String id) {
          return this.getMappedStatement(id, true);
        }
        
        public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
          if (validateIncompleteStatements) {
            buildAllStatements();
          }
          return mappedStatements.get(id);
        }
}
 
 
 # 四大对象之一
public class ParamNameResolver {
  // 如果是集合/数组就包装一下,这也就是为啥我们传入的参数是集合类型时可以通过 list 当作名字来获取。数组也同理 array
  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }
}
  1. configuration.getMappedStatement() 从全局配置类中根据名字获取 MappedStatement 对象

    1.1 wrapCollection() 包装参数 会调用 ParamNameResolver#wrapToMapIfCollection

  2. executor.query() 通过执行器执行query方法 (四大核心对象之一露面了)

    2.1 wrapCollection() 包装参数 会调用 ParamNameResolver#wrapToMapIfCollection

调用 CachingExecutor#query() 来查询

java 复制代码
  private final Executor delegate;

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 把参数传进去,构建一个 BoundSql 对象
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    // 创建一个缓存key 这个key超级长
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    // 查询
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

继续调用重载的方法:org.apache.ibatis.executor.CachingExecutor#query() 来查询。这时候会查询二级缓存

java 复制代码
  private final Executor delegate;

  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;
      }
    }
    // 继续调用查询,这次的executor是BaseExceutor
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

继续调用:BaseExecutor#query() 再查一下一级缓存

java 复制代码
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      // 查询一级缓存
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 查询数据库
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

上面两个地方就告诉了我们,mybatis的缓存为啥是先查二级再查一级

org.apache.ibatis.executor.BaseExecutor#queryFromDatabase() 查询数据库

java 复制代码
  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      // 执行查询
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    // 把结果缓存在一级缓存中
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

org.apache.ibatis.executor.SimpleExecutor#doQuery():

java 复制代码
  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 对象 默认是 PreparedStatementHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 获取一个 PreparedStatement 对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行SQL语句并通过ResultSetHandler处理返回结果
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

上面这个 doQuery() 方法的步骤分析:

第一步分析、configuration.newStatementHandler():

java 复制代码
   // org.apache.ibatis.session.Configuration类
   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);
    // 执行插件的 plugin() 方法
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
 
  // RoutingStatementHandler类
  private final StatementHandler delegate;
 
  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 判断一下要创建什么类型的 Statement
    switch (ms.getStatementType()) {
      // Statement
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      // PreparedStatement 预编译的 默认
      case PREPARED:
        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());
    }
  }
 
  // PreparedStatementHandler 类
  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 调用父类(BaseStatementHandler)的构造方法创建
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }
 
 // BaseStatementHandler类
 protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 全局配置
    this.configuration = mappedStatement.getConfiguration();
    // 执行器
    this.executor = executor;
    // MappedStatement    还记得吗?一个增删改查标签就是一个MappedStatement对象
    this.mappedStatement = mappedStatement;
    // 逻辑分页
    this.rowBounds = rowBounds;
    
    // 类型处理器的注册    里面是java类型和数据库类型的映射关系
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();
 
    if (boundSql == null) { // issue #435, get the key before calculating the statement
      // 自动生成主键
      generateKeys(parameterObject);
      // SQL对象
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }
 
    this.boundSql = boundSql;
 
    // 四大对象之一:参数处理器
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    // 四大对象之一:结果集处理器
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }
 
 
  // InterceptorChain类    插件
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

总结:

1、创建StatementHandler时会根据对应的类型创建

2、创建StatementHandler时会填充一堆属性的值 包括(ParameterHandler、ResultSetHandler、TypeHandlerRegistry) TypeHandlerRegistry中注册了一堆TypeHandler。ParameterHandler、ResultSetHandler就是通过TypeHandler来做类型处理的

4、创建对应的对象时会执行对应的plugin()方法 如:StatementHandler、ParameterHandler、ResultSetHandler

第二步分析:SimpleExecutor#prepareStatement():ParameterHandler

java 复制代码
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 获取连接
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    // ParameterHandler 设置参数
    handler.parameterize(stmt);
    return stmt;
  }

第三步分析:handler.query():ResultSetHandler

通过 RoutingStatementHandler#query() 调用到这里

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

总结

流程图

类关系图

相关推荐
小鸡脚来咯13 小时前
springboot 整合mybatis
java·spring boot·mybatis
种树人2024081914 小时前
MyBatis xml 文件中 SQL 语句的小于号未转义导致报错
mybatis
码农派大星。16 小时前
MyBatis操作--进阶
mybatis
爱读源码的大都督17 小时前
MyBatis中的LanguageDriver的作用是什么
java·spring boot·mybatis
鹿屿二向箔17 小时前
基于SSM(Spring + Spring MVC + MyBatis)框架的快递管理系统
spring·mvc·mybatis
2的n次方_18 小时前
MyBatis——增删查改(XML 方式)
xml·数据库·mybatis
haozihua1 天前
4.Mybatis中,在Mapper的SQL映射文件中,使用<choose><when>无法识别参数的情况
java·sql·mybatis
Onlooker1292 天前
MyBatis5-缓存
缓存·mybatis
天幕繁星2 天前
JSqlParser、JavaCC实操
mybatis·mybatis plus·jsqlparser·javacc
漫天转悠2 天前
SQL注入攻击及其在SpringBoot中使用MyBatisPlus的防范策略
spring boot·mybatis