面试官问你MyBatis SQL是如何执行的?把这篇文章甩给他(四)

简介: MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。

ParameterHandler 介绍

ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法

微信图片_20220412201415.png

ParameterHandler 只有一个实现类 DefaultParameterHandler , 它实现了这两个方法。

  • getParameterObject:用于读取参数
  • setParameters: 用于对 PreparedStatement 的参数赋值

ParameterHandler 的解析过程

上面我们讨论过了 ParameterHandler 的创建过程,下面我们继续上面 parameterSize 流程

微信图片_20220412201420.jpg

这就是具体参数的解析过程了,下面我们来描述一下

public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  // parameterMappings 就是对 #{} 或者 ${} 里面参数的封装
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    // 如果是参数化的SQL,便需要循环取出并设置参数的值
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      // 如果参数类型不是 OUT ,这个类型与 CallableStatementHandler 有关
      // 因为存储过程不存在输出参数,所以参数不是输出参数的时候,就需要设置。
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        // 得到 #{}  中的属性名
        String propertyName = parameterMapping.getProperty();
        // 如果 propertyName 是 Map 中的key
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          // 通过key 来得到 additionalParameter 中的value值
          value = boundSql.getAdditionalParameter(propertyName);
        }
        // 如果不是 additionalParameters 中的key,而且传入参数是 null, 则value 就是null
        else if (parameterObject == null) {
          value = null;
        }
        // 如果 typeHandlerRegistry 中已经注册了这个参数的 Class 对象,即它是 Primitive 或者是String 的话
        else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          // 否则就是 Map
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // 在通过 SqlSource 的parse 方法得到parameterMappings 的具体实现中,我们会得到parameterMappings 的 typeHandler
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        // 获取 typeHandler 的jdbc type
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          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);
        }
      }
    }
  }
}

下面用一个流程图表示一下 ParameterHandler 的解析过程,以简单执行器为例

微信图片_20220412201425.jpg

我们在完成 ParameterHandler 对 SQL 参数的预处理后,回到 SimpleExecutor 中的 doQuery 方法

微信图片_20220412201430.jpg

上面又引出来了一个重要的组件那就是 ResultSetHandler,下面我们来认识一下这个组件

ResultSetHandler

ResultSetHandler 简介

ResultSetHandler 也是一个非常简单的接口

微信图片_20220412201433.jpg

ResultSetHandler 是一个接口,它只有一个默认的实现类,像是 ParameterHandler 一样,它的默认实现类是DefaultResultSetHandler

ResultSetHandler 解析过程

MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,DefaultResultSetHandler 主要负责处理两件事

  • 处理 Statement 执行后产生的结果集,生成结果列表
  • 处理存储过程执行后的输出参数

按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。

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;
// 获取第一个结果集
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 获取结果映射
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
// 结果映射的大小
int resultMapCount = resultMaps.size();
// 校验结果映射的数量
validateResultMapsCount(rsw, resultMapCount);
// 如果ResultSet 包装器不是null, 并且 resultmap 的数量 > resultSet 的数量的话
// 因为 resultSetCount 第一次肯定是0,所以直接判断 ResultSetWrapper 是否为 0 即可
while (rsw != null && resultMapCount > resultSetCount) {
// 从 resultMap 中取出 resultSet 数量
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集, 关闭结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
// 从 mappedStatement 取出结果集
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);
}

其中涉及的主要对象有:

ResultSetWrapper : 结果集的包装器,主要针对结果集进行的一层包装,它的主要属性有

  • ResultSet : Java JDBC ResultSet 接口表示数据库查询的结果。有关查询的文本显示了如何将查询结果作为java.sql.ResultSet 返回。然后迭代此ResultSet以检查结果。
  • TypeHandlerRegistry: 类型注册器,TypeHandlerRegistry 在初始化的时候会把所有的 Java类型和类型转换器进行注册。
  • ColumnNames: 字段的名称,也就是查询操作需要返回的字段名称
  • ClassNames: 字段的类型名称,也就是 ColumnNames 每个字段名称的类型
  • JdbcTypes: JDBC 的类型,也就是 java.sql.Types 类型
  • ResultMap: 负责处理更复杂的映射关系

在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。

            </div>
目录
相关文章
|
3月前
|
SQL Oracle 关系型数据库
整合Mybatis-Plus高级,Oracle 主键Sequence,Sql 注入器实现自定义全局操作
整合Mybatis-Plus高级,Oracle 主键Sequence,Sql 注入器实现自定义全局操作
87 0
|
30天前
|
SQL Java 关系型数据库
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
16 0
|
30天前
|
SQL Java 关系型数据库
MyBatis中的9种常用动态sql标签精妙用法
MyBatis中的9种常用动态sql标签精妙用法
46 0
|
1月前
|
SQL Java 数据库连接
Mybatis拦截器实现带参数SQL语句打印
Mybatis拦截器实现带参数SQL语句打印
|
1月前
|
存储 缓存 Java
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
什么!?实战项目竟然撞到阿里面试的原题!???关于MyBatis Plus的缓存机制
|
1月前
|
SQL druid Java
【MyBatis】2、MyBatis 的动态 SQL 和增删改操作
【MyBatis】2、MyBatis 的动态 SQL 和增删改操作
29 0
|
2月前
|
SQL XML Java
【JavaEE进阶】 MyBatis之动态SQL
【JavaEE进阶】 MyBatis之动态SQL
|
2月前
|
存储 SQL 数据安全/隐私保护
Mybatis-Plus 拦截SQL语句实现加解密存储
Mybatis-Plus 拦截SQL语句实现加解密存储
35 0
|
3月前
|
SQL 缓存 Java
JAVAEE框架技术之8-myBatis ORM框架技术参数和动态SQL语句
JAVAEE框架技术之8-myBatis ORM框架技术参数和动态SQL语句
62 0
JAVAEE框架技术之8-myBatis ORM框架技术参数和动态SQL语句
|
3月前
|
SQL XML Java
【MyBatis】动态SQL
【MyBatis】动态SQL