您当前的位置: 首页 >  mybatis

white camel

暂无认证

  • 0浏览

    0关注

    442博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

MyBatis运行原理(三) : 多参数封装Map的流程、查询实现原理 (源码分析)

white camel 发布时间:2021-04-22 11:47:15 ,浏览量:0

四、MyBatis多个参数封装Map的过程

1、首先在该处打上断点 在这里插入图片描述 在这里插入图片描述 因为mapper是代理对象, 进入代理类MapperProxy中的invoke方法

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    // 判断是否是Object中的方法, method对象就是上面的getUser方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 主要进入该方法中
    return mapperMethod.execute(sqlSession, args);
  }

进入到MapperMethod类的execute方法, 因为是查询方法, 所以仅为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()) {
    result = executeForMap(sqlSession, args);
  } else if (method.returnsCursor()) {
    result = executeForCursor(sqlSession, args);
  } else {
  	// 主要看该方法, 转换参数
    Object param = method.convertArgsToSqlCommandParam(args);
    // 这里可以发现, 底层仍然使用的是selectOne方法
    result = sqlSession.selectOne(command.getName(), param);
    if (method.returnsOptional()
        && (result == null || !method.getReturnType().equals(result.getClass()))) {
      result = Optional.ofNullable(result);
    }
  }
  break;

进入convetArgsToSqlCommandParam方法的ParamNameResolver类的getNamedParams方法

在下面代码之前, 要搞懂ParamNameResolver类中private final SortedMap names;的值是怎么来的. 是在ParamNameResolver构造器中初始化的

public ParamNameResolver(Configuration config, Method method) {
	// 获取getUser方法的参数类型
  final Class[] paramTypes = method.getParameterTypes();
  // 获取getUser方法的注解
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  final SortedMap map = new TreeMap();
  int paramCount = paramAnnotations.length;
  // get names from @Param annotations
  for (int paramIndex = 0; paramIndex  12
    } else {
      final Map param = new ParamMap();
      int i = 0;
      for (Map.Entry entry : names.entrySet()) {
        // 循环后: param这个map中存放的就是: 
        // {id=args[0], name=args[1]}-->{id=12, name="lisi"}
        // 所以说@Param标注的参数, 底层仍然使用的是Map的方式, 采用#{key}来取值即可
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        // 下面的常量就是:param字符串
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
		  // 除了会存放上面的, param中还存放了{param1=12, param2="lisi"}
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      // {name=lisi, id=12, param1=12, param2=lisi}
      // 如此一来我们就可以#{key}或者#{param1}...#{paramN}的方式来取值了
      return param;
    }
  }

在这里插入图片描述

五、查询实现原理

在这里插入图片描述 1、因为mapper是一个代理对象, 实现了InvocationHandler, 所以在执行目标方法之前要执行invoke方法, 所以进入到Invoke方法

  @Override
  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 (method.isDefault()) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // method: public abstract com.sunny.domain.User com.sunny.mapper.UserMapper.getUserById(int)
    // 将method包装为mybatis能识别的MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 执行execute: args为传过来的参数12
    return mapperMethod.execute(sqlSession, args);
  }

2、进入MapperMethod的execute方法, 根据SQL语句, 选择SELECT分支

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:
        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 {
          // 这个将参数转换为sql命令参数的方法, 在后面会专门分析,多个参数转map的过程
          // 如果args为一个参数直接返回, 多个参数泽包装为一个map的过程
          Object param = method.convertArgsToSqlCommandParam(args);
          // 下面进入该方法, SqlCommand封装了SQL语句的信息
          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;
  }

3、进入的DefaultSqlSession的selectOne方法

@Override
public  T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  // 调用下面的方法
  List list = this.selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}

@Override
public  List selectList(String statement, Object parameter) {
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

@Override
public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 通过configuration获取MappedStatement, 因为ms封装了CRUD标签的详细信息
      // 并且包含了sqlSource的信息(如下图)
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 执行executor的查询方法
      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();
    }
  }

// 在executor.query之前, 先判断传进来的参数是否是Collection类型
// 下面的逻辑就标明了, 在编写mapper.xml中 List --> #{list}; Array --> #{array}的取值方式
private Object wrapCollection(final Object object) {
  if (object instanceof Collection) {
    StrictMap map = new StrictMap();
    map.put("collection", object);
    if (object instanceof List) {
      map.put("list", object);
    }
    return map;
  } else if (object != null && object.getClass().isArray()) {
    StrictMap map = new StrictMap();
    map.put("array", object);
    return map;
  }
  return object;
}

在这里插入图片描述

4、进入的CachingExecutor的query方法

@Override
public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取到sql语句的详细信息
  // BoundSql: SQL绑定, 封装SQL语句和对应的参数信息。
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

BoundSql内容 在这里插入图片描述 5、再进入上面return query方法

@Override
public  List 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 list = (List) 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;
    }
  }
  // 此时的delete就是你全局配置的那个执行器,这里没有配置默认为: SimpleExecutor
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

6、进入BaseExecutor的query方法

@Override
public  List 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 list;
  try {
    queryStack++;
    // 从一级缓存中拿, 这也印证了先从二级缓存拿, 然后再一级缓存
    list = resultHandler == null ? (List) 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;
}

7、进入到queryFromDatabase方法

  private  List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List list;
    // 往一级缓存存放数据, value为占位符
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    // 当查询出数据后, 再将查出的数据作为value存放到map
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }

在这里插入图片描述 8、进入到BaseExecutor的doQuery方法, 走的是子类SimpleExecutor的doQuery方法

  @Override
  public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    // 原生JDBC的Statement
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 创建了StatementHandler对象: 这里为PreparedStatementHandler对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 下面进入到RoutingStatementHandler来判断创建的StatementHandler
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 创建的statementHandler类型为PreparedStatementHandler,
    // 会经过拦截器链进行包装增强(类似aop的功能)
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }


  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

	// 从MeppedStatement中获取StatementType的类型
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        // 如果没有设置, 默认为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());
    }
  }

// 可以在crud标签中设置, 默认为PREPARED预编译的方式  
public enum StatementType {
  STATEMENT, PREPARED, CALLABLE
}
// 在创建PreparedStatementHandler对象的时候, 使用super调用父类的构造器, 
// 同时也将parameterHandler, resultSetHandler也创建了: 下面会使用到这俩对象
  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

// PreparedStatementHandler父类BaseStatementHandler的构造器
  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;

	// 创建下面俩对象之后, 仍然会经过拦截器链进行包装增强然后返回对象(类似aop的功能)
   // statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

9、返回到SimpleExecutor类doQuery方法, 并进入prepareStatement方法

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  // 拿到一个连接, 进行参数预编译设置到stmt中, handler:PrepareStatementHandler
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 进行参数预编译
  handler.parameterize(stmt);
  return stmt;
}

在这里插入图片描述

10、进入到 PreparedStatementHandler的parameterize方法: 进行参数预编译

  @Override
  public void parameterize(Statement statement) throws SQLException {
    // 会调用parameterHandler设置参数
    parameterHandler.setParameters((PreparedStatement) statement);
  }

11、进入DefaultParameterHandler的setParameters方法

@Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i  resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

18、进入到handleResultSet方法, 再进入handleRowValues方法, 再进入handleRowValuesForSimpleResultMap方法, 再进入getRowValue方法, 再进入applyAutomaticMappings方法

  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
      for (UnMappedColumnAutoMapping mapping : autoMapping) {
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        if (value != null) {
          foundValues = true;
        }
        if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
          // gcode issue #377, call setter on nulls (value is not 'found')
          metaObject.setValue(mapping.property, value);
        }
      }
    }
    return foundValues;
  }

19、在设置上面metaObject.setValue之前, 要进行数据库列类型和JavaBean类型做映射。

  private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
      throws SQLException {
    if (propertyMapping.getNestedQueryId() != null) {
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (propertyMapping.getResultSet() != null) {
      addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
      return DEFERRED;
    } else {
      final TypeHandler typeHandler = propertyMapping.getTypeHandler();
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      // 同样采用typeHandler来处理参数映射
      return typeHandler.getResult(rs, column);
    }
  }

// IntegerTypeHandler中的getNullableResult来获取columnName列的值
  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }

// 其他字段同理这样获取, 只是采用的TypeHandler不同, 然后都通过
// metaObject.setValue(mapping.property, value);设置给metaObject

在这里插入图片描述

最终取出数据设置给JavaBean对象 在这里插入图片描述

20、最后将查询到的数据赋值给JavaBean对象 在这里插入图片描述 在这里插入图片描述

查询流程总结: 在这里插入图片描述 在这里插入图片描述

1、获取sqlSessionFactory对象:
		解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
		注意:【MappedStatement】:代表一个增删改查的详细信息

2、获取sqlSession对象
		返回一个DefaultSQlSession对象,包含Executor和Configuration;
		这一步会创建Executor对象;

3、获取接口的代理对象(MapperProxy)
		getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象
		代理对象里面包含了,DefaultSqlSession(Executor)
4、执行增删改查方法

总结:
	1、根据配置文件(全局,sql映射)初始化出Configuration对象
	2、创建一个DefaultSqlSession对象,
		他里面包含Configuration以及
		Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
 4、MapperProxy里面有(DefaultSqlSession);
 5、执行增删改查方法:
 		1)、调用DefaultSqlSession的增删改查(Executor);
 		2)、会创建一个StatementHandler对象。
 			(同时也会创建出ParameterHandler和ResultSetHandler)
 		3)、调用StatementHandler预编译参数以及设置参数值;
 			使用ParameterHandler来给sql设置参数
 		4)、调用StatementHandler的增删改查方法;
 		5)、ResultSetHandler封装结果
 注意:
 	四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);
关注
打赏
1661428283
查看更多评论
立即登录/注册

微信扫码登录

0.0574s