• 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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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);
        }
    
    • 1
    • 2
    • 3

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

    image

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

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

    CLC

  • 相关阅读:
    Linux常见面试题
    Nacos服务注册与发现
    Windows系统提权姿势
    华为云云耀云服务器L实例评测|Ubuntu云锁防火墙安装搭建使用
    Redis Cluster 模式 的具体实施细节是什么样的?
    ARM_day8:温湿度数据采集应用
    leecode #加一#二进制求和
    使用grubby更改RHEL7/8/9的默认内核
    [NAS] Synology (群晖) DSM同步数据到阿里云盘
    内存管理总结
  • 原文地址:https://blog.csdn.net/xplan5/article/details/126431763