本文共 8774 字,大约阅读时间需要 29 分钟。
在 Mybatis 中,当 Integer 入参为 0 时,发现判断条件的非空判断没有生效,原本应该存在的判断条件丢失了。这意味着 Mybatis 对 Integer 参数进行了某种特殊处理,导致 0 被误判为有效值。
@GetMapping("/queryByAgeGroup")public HttpStatus queryByAgeGroup(@RequestParams("ageGroup") Integer ageGroup) { IndexTestService.queryByAgeGroup(ageGroup); return HttpStatus.HTTP_OK;} 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/