Mybatis 处理器Handler分类及介绍

处理器定位

前面我们分析过Mybatis的Executor,其实执行器还是调用StatementHandler来执行数据库操作。Mybatis中有3种类型的hanlder,分别为StatementHandler、ParameterHandler、ResultSetHandler。ParameterHandler主要解析参数,为Statement设置参数,ResultSetHandler主要是负责把ResultSet转换成Java对象。下面我们通过个时序图来区分下各自的作用。
在这里插入图片描述
看完时序图后大家应该可以知道这几个Handler的作用了。

ParameterHandler

ParameterHandler主要是解析Sql参数和用户传入参数,然后2者进行关联并设置到PreparedStatement中去。我们先看下ParameterHandler类。

public interface ParameterHandler {
  //获取参数方法
  Object getParameterObject();
  //把参数设置到PreparedStatement中
  void setParameters(PreparedStatement ps)
      throws SQLException;

}

子类实现类图
在这里插入图片描述
从上图可知,ParameterHandler只有一个实现类。我们现在分析下这个类。

// An highlighted block
public class DefaultParameterHandler implements ParameterHandler {
  //参数类型转换器的注册表,如果参数的类型在参数类型转换器的注册表中存在,
  //则会根据具体的转换类型进行转换,这块代码相对简单写自己看下就明白了。
  private final TypeHandlerRegistry typeHandlerRegistry;
  //MappedStatement简单理解就是我们一个查询,或者说是我们Mapper里面的一个方法。
  //主要保存这我们的SQL信息,参数信息、resultMap信息等,他主要是对一个SQl的描素信息。
  private final MappedStatement mappedStatement;
  //查询的参数信息
  private final Object parameterObject;
  //保存着sql脚本,参数已经被替换掉的sql脚本
  //参数的映射信息、参数信息
  private final BoundSql boundSql;
  //全局的configuration
  private final Configuration configuration;
  //构造方法
  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    //从boundSql列表中获取所有的参数映射列表,
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        //参数为非输出类型
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          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())) {
            value = parameterObject;
          } else {
            //MetaObject是Mybatis映射的核心,这里通过创建一个MetaObject对象,可以轻易的获取到对象的属性
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          //获取参数类型处理器
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          //如果参数为空,获取jdbcType类型,这个会把java类型的null转成成数据库能识别的null
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            //通过typeHandler把参数设置到ParameterStatement里面去。
            //typeHandler里面有各种类型来处理数据类型的转换,有兴趣可以自己点进去看下。
            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);
          }
        }
      }
    }
  }

}

DefaultParameterHandler核心逻辑:1.解析Sql脚本配置的参数信息
2.解析传入参数
3.通过TypeHandler把2的参数进行对应的类型转换并设置到PreparedStatment中。
4.TypeHandler里面提供了各种数据转换规则,有兴趣自己可以去看看。

ResultSetHandler

ResultSetHandler主要是对查询的结果集进行处理。简单理解为把ResultSet转换成Java bean的过程。
这里面设置到关联查询,1对1,多对多的关联查询。博主对关联查询不太熟悉,所以只讲简单的ResultSet转换成Java bean转换。其他想关联查询,1对1,多对多查询博主这边也研究不是很透,不能误人子弟。
我们先看看ResultSetHandler接口,我们先看下接口信息。

// 接口提供了3个方法,在绝大多数情况下我们只会用第一方法,博主对其他2个方法也没有过多研究。
public interface ResultSetHandler {
  //我们99%的情况下都是使用这个方法来对结果集进行解析。下面我们会对这个方法进行分析。
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

我们先看下ResultSetHandler的实现类信息。
在这里插入图片描述
ResultSetHandler只有一个实现类就是DefaultResultSetHandler,单千万别小看了DefaultResultSetHandler,所有的结果集处理都是在这里完成。关联查询也都是在这里处理。下面我们对常用的结果集处理进行分析。
我们先看下调用的堆栈信息:从PreparedStatement到DefaultResultSetHandler解析的全过程。
在这里插入图片描述

// 整个调用涉及到的类型及方法信息,下面我们会对这些方法进行分析。下面会着重看下如何创建返回结果对象,解析结果集并设置值
org.apache.ibatis.executor.statement.PreparedStatementHandler#query
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSet
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValues
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue
org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyAutomaticMappings
// 这里是ResultHandler处理的入口
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    //通过Statement获取到ResultSet,并包装成ResultSetWrapper
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    //获取对应的Map映射信息
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    //验证
    validateResultMapsCount(rsw, resultMapCount);
    //开始处理结果集合
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

	```javascript
	// 这里是创建结果集返回对象
	private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    this.useConstructorMappings = false; // reset previous mapping result
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();
    //根据反射创建对象
    Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        // issue gcode #109 && issue #149
        if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
          resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
          break;
        }
      }
    }
    this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
    return resultObject;
  }

	```

	//这里应该是存储过程的,暂不分析
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    return collapseSingleResultList(multipleResults);
  }

最终会调用到applyAutomaticMappings方法,中间有对各种关联查询,1对1,1对多等处理,

// An highlighted block
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    //获取参数和结果集的映射列表信息
    List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (!autoMapping.isEmpty()) {
      //循环映射列表信息
      for (UnMappedColumnAutoMapping mapping : autoMapping) {
        //通过ResultSet获取到对应的列信息。
        //Mybatis提供了丰富的TypeHandler来解析结果集。这里自己去看下
        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设置属性,把数据库查询出来的结果集set到对象中。
          //这里是居于反射来实现的。MetaObject用法不舒服的朋友可以去熟悉一下。
          metaObject.setValue(mapping.property, value);
        }
      }
    }
    return foundValues;
  }

如果不懂MetaBObject的同学可以去科普一下。

StatementHandler

前面已经讲过ParameterHandler、ResultSetHandler(这里代码比较复杂,讲得比较范)。ParameterHandler、ResultSetHandler都是为StatementHandler服务的。StatementHandler的实现比ParameterHandler、ResultSetHandler要多很多,我们先看下StatementHandler接口。

// 预处理
Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  void parameterize(Statement statement)
      throws SQLException;
  //批处理方法
  void batch(Statement statement)
      throws SQLException;
  //更新或删除操作
  int update(Statement statement)
      throws SQLException;
 //查询
  <E> List<E> query(Statement statement, ResultHandler resultHandler)
      throws SQLException;
  //流标查询
  <E> Cursor<E> queryCursor(Statement statement)
      throws SQLException;
  //绑定SQL信息、包括Sql,参数等
  BoundSql getBoundSql();
  ///虎丘参数处理器
  ParameterHandler getParameterHandler();

下面我们看下整体的结构图。
在这里插入图片描述
这个结构是不是看起来和前面讲的Executor很相似啊。没错都是套路。
StatementHandler:接口,定义了基本的方法。
RoutingStatementHandler:这个类就是根据不同的类型创建不同的StatementHandler,可以理解为简单工厂模式。
CallableStatementHandler:执行存储过程的StatementHandler。
PreparedStatementHandler:预处理的StatementHandler。
SimpleStatementHandler:简单处理器
上面三种处理器我们着重看下PreparedStatementHandler的query和update方法。

// 执行查询操作
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    //这里的statement是经过DefaultParameterHandler处理过的statement
    PreparedStatement ps = (PreparedStatement) statement;
    //执行查询操作
    ps.execute();
    //ResultHandler对结果集ResultSet进行处理
    return resultSetHandler.<E> handleResultSets(ps);
  }

我们来看下更新方法

// 更新方法
public int update(Statement statement) throws SQLException {
    //已经通过预编译的statement
    PreparedStatement ps = (PreparedStatement) statement;
    //执行更新操作
    ps.execute();
    //获取影响的行
    int rows = ps.getUpdateCount();
    //获取绑定的参数,这里是请求参数
    Object parameterObject = boundSql.getParameterObject();
    //获取Mapper文件配置是否要获取主键
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    //根据配置设置刚才更新或者新增数据的主键
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

总结

Mybatis中的执行器实际是调用处理来进行JDBC处理。处理器分为3类:1.StatementHandler处理器,2.ParameterHandler处理器,3.ResultSetHandler处理器。
StatementHandler调用ParameterHandler进行参数解析和预编译处理。
StatementHandler调用ResultSetHandler进行结果集映射。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值