源码哥:mybatis参数赋值->ParameterHandler

mybatis框架会自动的将各种类型的参数,赋值给参数占位符。 我们今天看下参数处理器的核心逻辑,感兴趣的小伙伴找到源码中的DefaultParameterHandler类, 它实现了ParameterHandler接口。

ParameterHandler定义了两个方法:

getParameterObject用于获取执行Mapper时传入的参数对象;

setParameters用于为PreparedStatement或者CallableStatement对象设置参数值。

setParameters方法源码解读

ini 复制代码
@Override
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  
  //首先获取Mapper配置中的参数映射ParameterMapping;
  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;
        } 
        // 如果类型处理器中有对应的typeHandler,直接赋值
        else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
        // 如果没有对应的typeHandler,将参数转化为MetaObject反射工具类 
        // 然后从元数据类中寻找当前参数的值
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // 获取typeHandler类型处理器
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          // 调用typeHandler的setParameter方法完成占位符参数的赋值
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException | SQLException e) {
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}

结合源代码,可以概括为下述几个步骤:

从BoundSql类中获取当前SQL语句中的占位符参数ParameterMapping定义,如果不为空就直接遍历ParameterMapping定义,为每一个占位符参数赋值;

如果占位符参数的ParameterMode不是OUT就进行赋值处理;

如果在类型注册Map中找到对应的typeHandler,就直接赋值

类型注册Map中有哪些类型呢,我们可以看下TypeHandlerRegistry类中的构造方法

scss 复制代码
public TypeHandlerRegistry(Configuration configuration) {
  this.unknownTypeHandler = new UnknownTypeHandler(configuration);

  register(Boolean.class, new BooleanTypeHandler());
  register(boolean.class, new BooleanTypeHandler());
  register(JdbcType.BOOLEAN, new BooleanTypeHandler());
  register(JdbcType.BIT, new BooleanTypeHandler());

  register(Byte.class, new ByteTypeHandler());
  register(byte.class, new ByteTypeHandler());
  register(JdbcType.TINYINT, new ByteTypeHandler());

  register(Short.class, new ShortTypeHandler());
  register(short.class, new ShortTypeHandler());
  register(JdbcType.SMALLINT, new ShortTypeHandler());

  register(Integer.class, new IntegerTypeHandler());
  register(int.class, new IntegerTypeHandler());
  register(JdbcType.INTEGER, new IntegerTypeHandler());

  register(Long.class, new LongTypeHandler());
  register(long.class, new LongTypeHandler());

  register(Float.class, new FloatTypeHandler());
  register(float.class, new FloatTypeHandler());
  register(JdbcType.FLOAT, new FloatTypeHandler());

  register(Double.class, new DoubleTypeHandler());
  register(double.class, new DoubleTypeHandler());
  register(JdbcType.DOUBLE, new DoubleTypeHandler());

  ......
  register(BigInteger.class, new BigIntegerTypeHandler());
  register(JdbcType.BIGINT, new LongTypeHandler());

  register(BigDecimal.class, new BigDecimalTypeHandler());
  register(JdbcType.REAL, new BigDecimalTypeHandler());
  register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
  register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

  register(InputStream.class, new BlobInputStreamTypeHandler());
  
  register(Date.class, new DateTypeHandler());
  register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
  register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
  register(JdbcType.TIMESTAMP, new DateTypeHandler());
  register(JdbcType.DATE, new DateOnlyTypeHandler());
  register(JdbcType.TIME, new TimeOnlyTypeHandler());

  register(java.sql.Date.class, new SqlDateTypeHandler());
  register(java.sql.Time.class, new SqlTimeTypeHandler());
  register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

  register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

  register(Instant.class, new InstantTypeHandler());
  register(LocalDateTime.class, new LocalDateTimeTypeHandler());
  register(LocalDate.class, new LocalDateTypeHandler());
  register(LocalTime.class, new LocalTimeTypeHandler());
  register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
  register(OffsetTime.class, new OffsetTimeTypeHandler());
  register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
  register(Month.class, new MonthTypeHandler());
  register(Year.class, new YearTypeHandler());
  register(YearMonth.class, new YearMonthTypeHandler());
  register(JapaneseDate.class, new JapaneseDateTypeHandler());

  // issue #273
  register(Character.class, new CharacterTypeHandler());
  register(char.class, new CharacterTypeHandler());
}

如果在没有找到对应的typeHandler,将参数转化为MetaObject反射工具类,然后从元数据类中寻找当前参数的值;

得到value值之后,再获取在ParameterMapping中定义的typeHandler类型处理器和Java类型; 调用typeHandler类型处理器的setParameter方法,将value值设置到当前的PreparedStatement对象。

我们这里看一个BigInt的处理类:

java 复制代码
public class BigIntegerTypeHandler extends BaseTypeHandler<BigInteger> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, BigInteger parameter, JdbcType jdbcType) throws SQLException {
    ps.setBigDecimal(i, new BigDecimal(parameter));
  }

  @Override
  public BigInteger getNullableResult(ResultSet rs, String columnName) throws SQLException {
    BigDecimal bigDecimal = rs.getBigDecimal(columnName);
    return bigDecimal == null ? null : bigDecimal.toBigInteger();
  }

  @Override
  public BigInteger getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    BigDecimal bigDecimal = rs.getBigDecimal(columnIndex);
    return bigDecimal == null ? null : bigDecimal.toBigInteger();
  }

  @Override
  public BigInteger getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    BigDecimal bigDecimal = cs.getBigDecimal(columnIndex);
    return bigDecimal == null ? null : bigDecimal.toBigInteger();
  }
}
相关推荐
捂月3 分钟前
从零开始:使用 Spring Boot 开发图书管理系统
java·spring boot·后端
张彦峰ZYF3 分钟前
接口性能优化宝典:解决性能瓶颈的策略与实践
java·redis·分布式·后端·算法·性能优化·架构
码蜂窝编程官方16 分钟前
【含开题报告+文档+PPT+源码】基于SSM的电影数据挖掘与分析可视化系统设计与实现
java·vue.js·人工智能·后端·spring·数据挖掘·maven
吃一克鹅蛋1 小时前
Spring 框架环境搭建
java·后端·spring
冷眼Σ(-᷅_-᷄๑)3 小时前
如何将Asp.net Core站点部署到CentOS
后端·centos·asp.net
St_Ludwig3 小时前
C语言小撰特殊篇-assert断言函数
c语言·c++·后端·算法
YaYicho3 小时前
Mybatis入门
mybatis
techdashen3 小时前
Go与黑客(第四部分)
开发语言·后端·golang
河北小田4 小时前
基于 Java 注解实现 WebSocket 服务器端
后端·websocket·程序员
as_jopo4 小时前
-Dspring.profiles.active=dev与--spring.profiles.active=dev的区别
java·后端·spring