开发者

MyBatis ParameterHandler的具体使用

目录
  • 一、概述
  • 二、源码
    • 1 关键属性
    • 2.setParameters
    • 3.TypeHandler
      • 1.TypeHandler接口定义
      • 2. 内置TypeHandler体系
    • 4. JdbcTypeForNull 的作用
    • 三、小结

      一、概述

      书接上回,StatementHandler参数处理&结果处理,分别委托给ParameterHandler&ResultSetHandler(注意不是ResultHandler)。那么本文介绍一下ParameterHandler

      本系列的第二章,我们讲了MyBATis的参数处理,那个参数处理,是把Mapper中各种可能的参数封装为Map,而这个参数处理,是把Map参数,设置到JDBC的Statement中。

      二、源码

      MyBatis中ParameterHandler只有一个实现DefaultParameterHandler,我们通过源码来解析其工作原理。

      1 关键属性

      DefaultParameterHandler 的关键属性:

      • MappedStatement:包含SQL元信息;
      • parameterObject:Mapper方法传入的原始参数;
      • BoundSql:包含解析后的 SQL 语句和参数映射列表;
      • TypeHandlerRegistry:类型处理器注册表,用于编程客栈查找合适的 TypeHandler。
      public class DefaultParameterHandler implements ParameterHandler {
          private final MappedStatement mappedStatement;
          private final Object parameterObject;
          private final BoundSql boundSql;
          private final Configuration configuration;
          private final TypeHandlerRegistry typeHandlerRegistry;
      
          public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
              this.mappedStatement = mappedStatement;
              this.parameterObject = parameterObject;
              this.boundSql = boundSql;
              this.configuration = mappedStatement.getConfiguration();
              this.typeHandlerRegistry = configuration.getTypeHandlehttp://www.devze.comrRegistry();
          }
      }
      

      2.setParameters

      setParameters 是ParameterHandler的核心方法,负责将参数绑定到 PreparedStatement 中:

      @Override
      public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        // 获取SQL中的参数映射列表(每个元素对应一个占位符)
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
          // 遍历每个参数映射,依次绑定到PreparedStatement
          for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            // 忽略输出参数(存储过程场景)
            if (parameterMapping.getMode() != ParameterMode.OUT) {
              Object value;
              String propertyName = parameterMapping.getProperty(); // 参数名称(如"id"、"user.name")
              // 获取参数值:从原始参数对象中解析propertyName对应的 value
              if (boundSql.hasAdditionalParameter(propertyName)) {
                // 从附加参数中获取(<foreach>或<bind>标签可以提供额外参数)
                value = boundSql.getAdditionalParameter(propertyName);
              } else if (parameterObject == null) {
                value = null; // 参数为null时直接设为null
              } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                // 参数本身就是TypeHandler可以处理的类型
                value = parameterObject;
              } else {
                // 参数是复杂对象,通过反射获取propertyName对应的属性值
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                value = metaObject.getValue(propertyName);
              }
              // 获取参数对应的TypeHandler(负责Java类型→JDBC类型的转换)
              TypeHandler typeHandler = parameterMapping.getTypeHandler();
              JdbcType jdbcType = parameterMapping.getJdbcType();
              if (value == null && jdbcType == null) {
                jdbcType = configuration.getJdbcTypeForNull();
              }
              try {
                // 调用TypeHandler的方法,将value绑定到PreparedStatement的第i+1个占位符
                typeHandler.setParameter(ps, i + 1, value, jdbcType);
              } catch (SQLException e) {
                throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
              }
            }
          }
        }
      }
      

      这段代码的执行流程可分为三步:

      1. 解析参数映射列表(ParameterMapping),每个元素对应 SQL 中的一个占位符
      2. 根据参数名称从参数对象中获取参数值(支持复杂对象的属性访问)
      3. 通过 TypeHandler 将参数值转换为 JDBC 类型并绑定到 PreparedStatement

      AdditionalParameter这个额外参数是什么?

      1. foreach标签中的item、index都会被解析为额外参数;
      2. binhttp://www.devze.comd标签,也可以提供额外参数,你可以利用bind标签,使用OGNL表达式以外创建一个变量,并将其绑定到当前的上下文。

      3.TypeHandler

      TypeHandler(类型处理器)负责Java类型与JDBC类型之间的转换,不只是ParameterHandler处理参数需要他,ResultSetHandler处理返回结果时也需要他。

      1.TypeHandler接口定义

      通过接口定义就可以看出来,TypeHandler负责JAVA类型与JDBC类型互相转换。

      public interface TypeHandler<T> {
          void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
          
          T getResult(ResultS编程客栈et rs, String columnName) throws SQLException;
          
          T getResult(ResultSet rs, int columnIndex) throws SQLException;
          
          T getResult(CallableStatement cs, int columnIndex) throws SQLException;
      }
      

      2. 内置TypeHandler体系

      MyBatis 提供了丰富的内置 TypeHandler,这里的例子只是冰山一角,有兴趣可以看下源码,一般情况下,是不需要自己自定义类型转换器的。具体实现这里不深入了,比较简单,模板模式,BaseTypeHandler实现了一些通用逻辑,子类就是JDBC API的各种调用。有兴趣可以直接看源码。

      类型分类典型实现类功能说明
      基本类型IntegerTypeHandler处理 Integer 与 JDBC INTEGER 转换
      字符串类型StringTypeHandler处理 String 与 JDBC VARCHAR 转换
      日期类型LocalDateTimeTypeHandler处理 Java 8 日期类型与 JDBC 日期类型转换
      枚举类型EnumTypeHandler处理枚举的名称与 JDBC 类型转换
      集合 / 数组类型ArrayTypeHandler处理 Java 数组与 JDBC ARRAY 类型转换

      4. JdbcTypeForNull 的作用

      jdbcTypeForNull是 MyBatis中用于处理null值参数的重要配置,当参数值为 null且未指定具体JDBC 类型时生效。

      其主要作用是解决不同数据库对 null 值处理的兼容性问题,某些数据库,对 null 值的类型非常敏感,需要明确指定 JDBC 类型,比如oracle,需要设置为NULL

      可以在XML中配置

      <configuration>
          <settings>
              <!-- 设置null值默认的JDBC类型 -->
              <setting name="jdbcTypeForNull" value="NULL"/>
          </settings>
      </configuration>
      

      三、小结

      ParameterHandler是MyBatis处理Statement的参数的核心组件,通过与 TypeHandler协作,实现了Java 类型到JDBC类型的灵活转换,支撑了MyBatis对各种复杂参数场景的处理能javascript力。

      到此这篇关于MyBatis ParameterHandler的具体使用的文章就介绍到这了,更多相关MyBatis ParameterHandler内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜