mybatis字段映射的容错性

mybatis字段映射的容错性

起因从一个bug说起,大致经历如下:

经过一次selectById,然后用查出来的数据做updateById。
数据的值 由 '' --> 0;
找遍相关代码,没有发现什么地方有setType(0)的动作,然后怀疑mybatis查询时的字段映射做了容错处理,将''转换为Integer的0。
复现:造一条数据,数据库值为'',然后selectById,发现得到的type确实是0;

为了验证猜想,对跟踪了mybatis的相关源码。

mybatis在查询完成数据库后,需要进行字段映射。
mybatis字段映射的任务有:

  • 数据库属性与java属性的映射
  • 数据库类型与java类型的转换

类型的自动转换里有一些猫腻,在做类型转换的时候,如果遇到困难(类型不匹配),可以选择抛异常,也可以选择容错。而mybatis选择的是容错。容错是一中缺乏严谨性的策略。

从一个例子说起字段映射的容错性

java类型
private Integer type;
mysql类型
type` char(1) DEFAULT '' COMMENT '类型 (1:KA 和 2:PA)',

mybatis处理字段映射的逻辑主要是在DefaultResultSetHandler
org.apache.ibatis.executor.resultset.DefaultResultSetHandler

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
        ensureNoRowBounds();
        checkResultHandler();
        handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
        // 简单类型的映射都会进入这里
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

image

image

根据不同的类型,进入不同的TypeHandler.

mybatis提供的Handler有如下几种。

image

如Integet类型就被IntegerTypeHandler

此处用的方法如下,ResultSet.getInt

  @Override
  public Integer getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }

int result = rs.getInt(columnName);

image

代码跟踪到此,可发现mysql查询的结果为空字符串,也就是 '\u0000',和数据库值是一致的。

image

最后围绕在一个复杂的三目运算符

 public int resultSet_getInt(ResultSetProxy rs, String columnLabel) throws SQLException {
        return this.pos < this.filterSize ? this.nextFilter().resultSet_getInt(this, rs, columnLabel) : rs.getResultSetRaw().getInt(columnLabel);
    }

最终在一个getInt的方法里找到了答案,默认赋0;

image

此处可以得到结论,mybatis的字段映射时,会有少量的容错处理,将数据库的CHAR类型的 空字符串 转换Integer做了容错,没有抛异常,也没有返回null,选择的是int的默认值0。
同理如果其它类型,也会有相关的默认值容错,如getDouble的0.0;getBigDecimal的new BigDecimal(0);...

根本性的避免此类问题的发生,还需数据库字段类型和java的类型尽量保持一致。

posted @ 2022-08-19 20:57  路迢迢  阅读(80)  评论(0编辑  收藏  举报