博客
关于我
源码解析之 Mybatis 对 Integer 参数做了什么手脚?
阅读量:439 次
发布时间:2019-03-06

本文共 8611 字,大约阅读时间需要 28 分钟。

源码解析之 Mybatis 对 Integer 参数做了什么手脚?

问题描述

在 Mybatis 中,当 Integer 入参为 0 时,发现判断条件的非空判断没有生效,原本应该存在的判断条件丢失了。这意味着 Mybatis 对 Integer 参数进行了某种特殊处理,导致 0 被误判为有效值。

接口示例

@GetMapping("/queryByAgeGroup")public HttpStatus queryByAgeGroup(@RequestParams("ageGroup") Integer ageGroup) {    IndexTestService.queryByAgeGroup(ageGroup);    return HttpStatus.HTTP_OK;}

查询 SQL 示例

数据表结构示例

CREATE TABLE `people_info` (    `id` varchar(64) NOT NULL COMMENT '主键',    `name` varchar(255) DEFAULT NULL COMMENT '名称',    `age_group` int(11) DEFAULT NULL COMMENT '年龄',    PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

环境示例

当入参为 0 时,生成的 SQL 为:

select *from `people_info`where 1 = 1-- 本该存在的 ageGroup 判断消失了!

源码解析

解析过程

  • DefaultSqlSession#select

    @Overridepublic void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {    try {        MappedStatement ms = configuration.getMappedStatement(statement);        executor.query(ms, wrapCollection(parameter), rowBounds, handler);    } catch (Exception e) {        throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);    } finally {        ErrorContext.instance().reset();    }}
  • BaseExecutor#query

    @Overridepublic List
    query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { return query(ms, parameter, rowBounds, resultHandler, key, boundSql, 0);}
  • MappedStatement#getBoundSql

    public BoundSql getBoundSql(Object parameterObject) {    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);    List
    parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } for (ParameterMapping pm : boundSql.getParameterMappings()) { String rmId = pm.getResultMapId(); if (rmId != null) { ResultMap rm = configuration.getResultMap(rmId); if (rm != null) { hasNestedResultMaps |= rm.hasNestedResultMaps(); } } } return boundSql;}
  • SqlSource#getBoundSql

    public interface SqlSource {    BoundSql getBoundSql(Object parameterObject);}
  • DynamicSqlSource#getBoundSql

    @Overridepublic BoundSql getBoundSql(Object parameterObject) {    DynamicContext context = new DynamicContext(configuration, parameterObject);    rootSqlNode.apply(context);    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);    Class
    parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql;}
  • SqlNode#apply

    public interface SqlNode {    boolean apply(DynamicContext context);}
  • IfSqlNode#apply

    @Overridepublic boolean apply(DynamicContext context) {    if (evaluator.evaluateBoolean(test, context.getBindings())) {        contents.apply(context);        return true;    }    return false;}
  • ExpressionEvaluator#evaluateBoolean

    public boolean evaluateBoolean(String expression, Object parameterObject) {    Object value = OgnlCache.getValue(expression, parameterObject);    if (value instanceof Boolean) {        return (Boolean) value;    }    if (value instanceof Number) {        return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0;    }    return value != null;}
  • OgnlCache#getValue

    public static Object getValue(String expression, Object root) {    try {        Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);        return Ognl.getValue(parseExpression(expression), context, root);    } catch (OgnlException e) {        throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e);    }}
  • Ognl#getValue

    public static Object getValue(Object tree, Map context, Object root) throws OgnlException {    return getValue(tree, context, root, null);}
  • TypeConverter#convertValue

    public interface TypeConverter {    public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType);}
  • DefaultTypeConverter#convertValue

    public Object convertValue(Map context, Object value, Class toType) {    return OgnlOps.convertValue(value, toType);}
  • OgnlOps#convertValue

    public static Object convertValue(Object value, Class toType) {    return convertValue(value, toType, false);}
  • OgnlOps#convertValue

    public static Object convertValue(Object value, Class toType, boolean preventNulls) {    Object result = null;    if (value != null && toType.isAssignableFrom(value.getClass())) {        return value;    }    if (value != null) {        if (value.getClass().isArray() && toType.isArray()) {            result = Array.newInstance(toType.getComponentType(), Array.getLength(value));            for (int i = 0, icount = Array.getLength(value); i < icount; i++) {                Array.set(result, i, convertValue(Array.get(value, i), toType.getComponentType()));            }        } else if (value.getClass().isArray() && !toType.isArray()) {            return convertValue(Array.get(value, 0), toType);        } else if (!value.getClass().isArray() && toType.isArray()) {            if (toType.getComponentType() == Character.TYPE) {                result = stringValue(value).toCharArray();            } else if (toType.getComponentType() == Object.class) {                if (value instanceof Collection) {                    return ((Collection) value).toArray(new Object[0]);                } else {                    return new Object[] { value };                }            }        }        if ((toType == Integer.class) || (toType == Integer.TYPE)) {            result = new Integer((int) longValue(value));        }        if ((toType == Double.class) || (toType == Double.TYPE)) {            result = new Double(doubleValue(value));        }        if ((toType == Boolean.class) || (toType == Boolean.TYPE)) {            result = booleanValue(value) ? Boolean.TRUE : Boolean.FALSE;        }        if ((toType == Byte.class) || (toType == Byte.TYPE)) {            result = new Byte((byte) longValue(value));        }        if ((toType == Character.class) || (toType == Character.TYPE)) {            result = new Character((char) longValue(value));        }        if ((toType == Short.class) || (toType == Short.TYPE)) {            result = new Short((short) longValue(value));        }        if ((toType == Long.class) || (toType == Long.TYPE)) {            result = new Long(longValue(value));        }        if ((toType == Float.class) || (toType == Float.TYPE)) {            result = new Float(doubleValue(value));        }        if (toType == BigInteger.class) {            result = bigIntValue(value);        }        if (toType == BigDecimal.class) {            result = bigDecValue(value);        }        if (toType == String.class) {            result = stringValue(value);        }    } else {        if (toType.isPrimitive()) {            result = OgnlRuntime.getPrimitiveDefaultValue(toType);        } else if (preventNulls && toType == Boolean.class) {            result = Boolean.FALSE;        } else if (preventNulls && Number.class.isAssignableFrom(toType)) {            result = OgnlRuntime.getNumericDefaultValue(toType);        }    }    if (result == null && preventNulls) {        return value;    }    if (value != null && result == null) {        throw new IllegalArgumentException("Unable to convert type " + value.getClass().getName() + " of " + value + " to type of " + toType.getName());    }    return result;}
  • OgnlOps#longValue

    public static long longValue(Object value) throws NumberFormatException {    if (value == null) return 0L;    Class c = value.getClass();    if (c.getSuperclass() == Number.class) return ((Number) value).longValue();    if (c == Boolean.class) return ((Boolean) value).booleanValue() ? 1 : 0;    if (c == Character.class) return ((Character) value).charValue();    return Long.parseLong(stringValue(value, true));}
  • 结论

    Mybatis 在处理 Integer 参数时,会将其转换为 Long 类型。具体来说,当 Integer 参数为 0 时,会被转换为 Long 类型的 0L。这种转换导致在非空判断中,0 被误判为有效值,从而使得原本应该存在的判断条件丢失。

    因此,当使用 Mybatis 的非空判断时,若参数为 Integer 类型且为 0,会被误判为有效值。需要在条件判断中特别处理 0,或者确保参数类型不会被不正确地转换。

    此外,类似的类型转换问题也会影响 Double、Byte、Character、Short、Long、Float、BigInteger 和 BigDecimal 等其他数值类型的参数。

    转载地址:http://iusyz.baihongyu.com/

    你可能感兴趣的文章
    Objective-C实现ID3贪心算法(附完整源码)
    查看>>
    Objective-C实现IIR 滤波器算法(附完整源码)
    查看>>
    Objective-C实现IIR数字滤波器(附完整源码)
    查看>>
    Objective-C实现insertion sort插入排序算法(附完整源码)
    查看>>
    Objective-C实现integer partition整数分区算法(附完整源码)
    查看>>
    Objective-C实现integerPartition整数划分算法(附完整源码)
    查看>>
    Objective-C实现interpolation search插值搜索算法(附完整源码)
    查看>>
    Objective-C实现Interpolation search插值查找算法(附完整源码)
    查看>>
    Objective-C实现intersection交集算法(附完整源码)
    查看>>
    Objective-C实现intro sort内省排序算法(附完整源码)
    查看>>
    Objective-C实现inverse matrix逆矩阵算法(附完整源码)
    查看>>
    Objective-C实现inversions倒置算法(附完整源码)
    查看>>
    Objective-C实现isalpha函数功能(附完整源码)
    查看>>
    Objective-C实现islower函数功能(附完整源码)
    查看>>
    Objective-C实现isPowerOfTwo算法(附完整源码)
    查看>>
    Objective-C实现isupper函数功能(附完整源码)
    查看>>
    Objective-C实现ItemCF算法(附完整源码)
    查看>>
    Objective-C实现ItemCF算法(附完整源码)
    查看>>
    Objective-C实现iterating through submasks遍历子掩码算法(附完整源码)
    查看>>
    Objective-C实现iterative merge sort迭代归并排序算法(附完整源码)
    查看>>