æºç 解æä¹ Mybatis 对 Integer åæ°åäºä»ä¹æèï¼
æ¬æ为深度é¿æï¼è¯·èå¿é 读ï¼
title: æºç 解æä¹ Mybatis 对 Integer åæ°åäºä»ä¹æèï¼
date: 2021-03-11
updated: 2021-03-11
categories:
- Mybatis
-
æºç 解æ
tags:
- Mybatis
- æºç 解æ
解å³æ¹æ¡æ¾å¨ç¬¬äºèï¼æ¥é解å³é®é¢ï¼å¯ç´æ¥æ¥ç解å³æ¹æ¡ã
æ¬æ为深度é¿æï¼è¯·èå¿é 读ï¼
é®é¢æè¿°
å¨ Mybatis ä¸ï¼Integer çå ¥å为 0 æ¶ï¼åç°å¤ææ¡ä»¶çé空å¤æ没æçæï¼åæ¬åºè¯¥åå¨çå¤ææ¡ä»¶ä¸¢å¤±äºã
é£ä¹ï¼Mybatis å°åºå¯¹ Integer åæ°åäºä»ä¹æèå¢ï¼ä¸é¢æ们æ¥ä¸¾ä¾è¯´æï¼
ç¯å¢ç¤ºä¾ï¼
该é®é¢åªä¸ Mybatis çå®ç°æºå¶æå ³ï¼ä¸çæ¬åºæ¬æ å ³ï¼å¦æ说ç¸å ³æ§ï¼å¯è½åªä¸æºç ä¸å®ç°ä»£ç æå¨çè¡æ°æå ³ï¼ã
ä¸è¿ï¼ä¸ºäºå »æè¯å¥½çä¹ æ¯ï¼è¿æ¯ç¨å¾®æä¸ä¸ï¼æ使ç¨ç Mybatis çæ¬æ¯ 3.5.2ã
æ¥å£ç¤ºä¾ï¼
@GetMapping("/queryByAgeGroup")
public HttpStatus queryByAgeGroup(@RequestParams("ageGroup") Integer ageGroup) {
// ageGroup å¹´é¾æ®µï¼0 代表幼å¿ï¼1 代表éå¹´ï¼2 代表ä¸å¹´ï¼3 代表èå¹´ï¼-1 代表æªç¥
IndexTestService.queryByAgeGroup(ageGroup);
return HttpStatus.HTTP_OK;
}
æµè¯ç¨ä¾å±äºåæ°éä¼ ï¼æ²¡æä¸å¡é»è¾ï¼æ çç¥ Service å Dao å±ã
æ¥è¯¢ SQL 示ä¾ï¼
<select id="queryByAgeGroup" text="">
select *
from `people_info`
where 1 = 1
<if test="ageGroup != null and ageGroup != ''">
and age_group = #{ageGroup}
</if>
</select>
æ°æ®è¡¨ç»æ示ä¾ï¼
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 å¤ææ¶å¤±äºï¼
解å³æ¹æ¡
é¦å æä¾è¯¥é®é¢çå ç§è§£å³æ¹æ¡ã
æ¹æ¡ä¸
å¦ææ¯æ°æ®åå ¸ç±»åçå段ï¼å¨å®ä¹æ°æ®åå ¸æ¶ï¼é¿å ä½¿ç¨ 0 ä½ä¸ºæ举å¼ï¼ä»æ ¹æºæç»è¯¥é®é¢ã
æ¹æ¡äº
å¦ææ¯éæ³æ°æ®ï¼å¯å¨ Controller å±å ¥åå¢å åæ°æ ¡éªï¼å¦æä¼ 0ï¼æ示âåæ°æ æâã
æ¹æ¡ä¸
å¦ææ¯åæ³æ°æ®ï¼å¯å¨ SQL å¤ææ¡ä»¶ä¸å¢å
or ageGroup == 0
å¤æã
<if test="ageGroup != null and ageGroup != '' or ageGroup == 0">
and age_group = #{ageGroup}
</if>
æ¹æ¡å
å¦ææ¯åæ³æ°æ®ï¼å¯å° Integer 转为 Stringï¼æ String åæ°å¤çã
String ageGroupStr = String.valueOf(1);
æºç 解æ
ä¸é¢ï¼æ们就éè¿åææºç ï¼ä¸èµ·æ¥çä¸ä¸ Mybatis ä¸ä¸ºäººç¥çâå°å¨ä½âã
解æ
- é¦å
ï¼è®©æ们æ¥å° DefaultSqlSession#select(statement, parameter, rowBounds, handler) æ¹æ³ã
// 第 165 è¡ @Override public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { // æ¿å°æ å°ç sql è¯å¥ MappedStatement ms = configuration.getMappedStatement(statement); // æ§è¡å¨æ§è¡æ¥è¯¢ sql -- éç¹ï¼ï¼ï¼ executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
- æ¤æ¶æ¿å°ä¼ å
¥ sqlï¼é£ä¹æâå°å¨ä½âçç¸æ³å¿
æ¯æ§è¡å¨ï¼ä¸é¢è¿å
¥ BaseExecutor#query(ms, parameter, rowBounds, resultHandler) æ¹æ³ã
// 第 132 è¡ @Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // sql ç»å® -- éç¹ï¼ï¼ï¼ BoundSql boundSql = ms.getBoundSql(parameter); // å建ç¼å key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // æ§è¡æ¥è¯¢ return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }
- å¨ sql ç»å®è¿ç¨ä¸é½åäºä»ä¹ï¼ä¸é¢è¿å
¥ MappedStatement#getBoundSql(parameterObject) æ¹æ³ã
// 第 296 è¡ public BoundSql getBoundSql(Object parameterObject) { // è·å sql ç»å® -- éç¹ï¼ï¼ï¼ BoundSql boundSql = sqlSource.getBoundSql(parameterObject); // è·ååæ°æ å°éå List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings == null || parameterMappings.isEmpty()) { boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject); } // check for nested result maps in parameter mappings (issue #30) 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; }
- å¯ä»¥çå° sql æ¯å¨ sqlSource ä¸è¢«ç»å®çï¼ä¸é¢è¿å
¥ SqlSource#getBoundSql(parameterObject) æ¹æ³ã
// 第 24 è¡ public interface SqlSource { BoundSql getBoundSql(Object parameterObject); }
- è¿æ¯ä¸ªæ¥å£æ¹æ³ï¼æ 4 ç§é»è®¤å®ç°ï¼DynamicSqlSourceãProviderSqlSourceãRawSqlSource å StaticSqlSourceã
- 以为 DynamicSqlSource 为ä¾ï¼è¿å
¥ DynamicSqlSource#getBoundSql(parameterObject) æ¹æ³ã
// 第 36 è¡ @Override public BoundSql getBoundSql(Object parameterObject) { // è·åä¸ä¸æ对象 DynamicContext context = new DynamicContext(configuration, parameterObject); // è£ è½½ sql æ ç¾èç¹ä¸çè¯å¥ -- éç¹ï¼ï¼ï¼ rootSqlNode.apply(context); // å建 sql æé å¨ SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); // 设置åæ°ç±»å Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); // 解æ sql SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); // è·åå·²ç»å®ç sql BoundSql boundSql = sqlSource.getBoundSql(parameterObject); // 循ç¯ç»å®åæ° context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; }
- è¿å
¥ SqlNode#apply(context) æ¹æ³ã
// 第 21 è¡ public interface SqlNode { boolean apply(DynamicContext context); }
- è¿ä¹æ¯ä¸ªæ¥å£æ¹æ³ï¼æ 8 ç§é»è®¤å®ç°ï¼ChooseSqlNodeãForEachSqlNodeãIfSqlNodeãMixedSqlNodeãStaticTextSqlNodeãTextSqlNodeãTrimSqlNode å VarDeclSqlNodeï¼åå«å¯¹åºä¸åç±»åçæ ç¾èç¹å¤çæ¹å¼ã
- è¿éæ们è¦æ¾çæ¯ if æ ç¾èç¹çå¤çé»è¾ï¼å æ¤è¿å
¥ IfSqlNode#apply(context) æ¹æ³ã
// 第 32 è¡ @Override public boolean apply(DynamicContext context) { // å¤æ -- éç¹ï¼ï¼ï¼ if (evaluator.evaluateBoolean(test, context.getBindings())) { // 继ç»è§£æåç»æ ç¾èç¹ contents.apply(context); return true; } return false; }
- è¿å
¥ ExpressionEvaluator#evaluateBoolean(expression, parameterObject) æ¹æ³ã
// 第 31 è¡ public boolean evaluateBoolean(String expression, Object parameterObject) { // è·ååæ°å¼ -- éç¹ï¼ï¼ï¼ // 注æï¼value ä¸ç®¡ä¹åæ¯ '' è¿æ¯ 0ï¼è¿éè¿åä¸å®æ¯ 0ï¼å ·ä½é»è¾å±ä»¬ç»§ç»åä¸çã Object value = OgnlCache.getValue(expression, parameterObject); // è¥åæ°å¼ä¸ºå¸å°ç±»å if (value instanceof Boolean) { // 强转为å¸å°å¼å¹¶è¿å return (Boolean) value; } // è¥åæ°å¼ä¸ºæ°å¼ç±»å if (value instanceof Number) { // 强转为å符串åï¼å转为 BigDecimalï¼ç¶åä¸ BigDecimal.ZERO æ¯è¾ï¼å¤ææ¯å¦ä¸ä¸º 0ï¼å¹¶è¿åå¤æç»æ // æ ¹æ®ä¸é¢çç»è®ºï¼å½ä¼ å ¥ '' å 0 èµ°å°è¿ä¸æ¥æ¶ï¼value å¼å¿ ç¶ä¸º 0ï¼é£ä¹è¿åç»æ为 falseï¼å æ¤è¯¥ if æ ç¾è¢«è·³è¿ return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0; } // å¤æåæ°å¼æ¯å¦ä¸ºç©ºï¼å¹¶è¿åå¤æç»æ return value != null; }
- è¿éæ们ä¼åç°Mybatis å®é
ä¸æ¯ä½¿ç¨ OGNL 表达å¼æ¥å¤çåæ°çï¼ä¸é¢è¿å
¥ OgnlCache#getValue(expression, root) éææ¹æ³ã
// 第 43 è¡ 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(tree, context, root) éææ¹æ³ã
// 第 454 è¡ public static Object getValue(Object tree, Map context, Object root) throws OgnlException { // 没ä»ä¹å¥½è§£éçï¼ç»§ç»è·è¿ return getValue(tree, context, root, null); } // 第 482 è¡ public static Object getValue(Object tree, Map context, Object root, Class resultType) throws OgnlException { Object result; // æ建 OGNL ä¸ä¸æ对象 OgnlContext ognlContext = (OgnlContext) addDefaultContext(root, context); // 强转èç¹å¯¹è±¡ Node node = (Node)tree; // è¥å¯åå¨ä¸ä¸ºç©º if (node.getAccessor() != null) // ä»å¯åå¨ä¸è·ååæ°å¼ result = node.getAccessor().get(ognlContext, root); else // å¦åï¼ä»èç¹å¯¹è±¡è·ååæ°å¼ result = node.getValue(ognlContext, root); // è¥åæ°ç±»åä¸ä¸ºç©º if (resultType != null) { // è·åç±»å转æ¢å¨ï¼è½¬æ¢åæ°å¼ -- éç¹ï¼ï¼ï¼ result = getTypeConverter(context).convertValue(context, root, null, null, result, resultType); } return result; }
- è¿ä¸æ¯åæ°è½¬æ¢çæ¹æ³åï¼è¿å
¥ TypeConverter#convertValue(context, target, member, propertyName, value, toType) æ¹æ³ã
public interface TypeConverter { public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType); }
- åæ¯æ¥å£æ¹æ³ï¼ä¸é¢è¿å
¥å®ç°ç±» DefaultTypeConverter#convertValue(context, target, member, propertyName, value, toType) æ¹æ³ã
// 第 48 è¡ -- åè¿å ¥è¿é public Object convertValue(Map context, Object value, Class toType) { // 转æ¢åæ°å¼ -- éç¹ï¼ï¼ï¼ return OgnlOps.convertValue(value, toType); } // 第 53 è¡ -- å è¿å ¥è¿é public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) { return convertValue(context, value, toType); }
- 离çç¸è¶æ¥è¶è¿äºï¼è¿å
¥ OgnlOps#convertValue(Object value, Class toType) æ¹æ³ã
// 第 508 è¡ -- å è¿å ¥è¿é public static Object convertValue(Object value, Class toType) { return convertValue(value, toType, false); } // 第 553 è¡ -- åè¿å ¥è¿é 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 array -> array then convert components of array individually */ if (value.getClass().isArray() && toType.isArray()) { // å¦æåæ°å¼ä¸åæ°ç±»åé½ä¸ºæ°ç» // è·åç±»å Class componentType = toType.getComponentType(); // æ ¹æ®ç±»ååé¿åº¦ï¼å建 Array 对象 result = Array.newInstance(componentType, Array.getLength(value)); // å¾ªç¯ Arrayï¼å¯¹ Array æ¯ä¸ä¸ªåæ°å¼è¿è¡è½¬æ¢å¹¶èµå¼ for(int i = 0, icount = Array.getLength(value); i < icount; i++) { Array.set(result, i, convertValue(Array.get(value, i), componentType)); } } 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) { // å¦æåæ°ç±»å为 char // å°åæ°å¼è½¬ä¸ºå符串åï¼è½¬ä¸º char æ°ç» result = stringValue(value).toCharArray(); } else if (toType.getComponentType() == Object.class) { // å¦æåæ°ç±»å为 Obejct if (value instanceof Collection) { // å¦æåæ°ç±»å为éå // 强转为éå Collection vc = (Collection) value; // 转æ¢ä¸ºæ°ç» return vc.toArray(new Object[0]); } else // å建ä¸ä¸ªæ°ç Object 对象并è¿å return new Object[] { value }; } } else { // å¦æåæ°ç±»å为 Integer -- éç¹ï¼ï¼ï¼ if ((toType == Integer.class) || (toType == Integer.TYPE)) { // åæ°å¼è½¬æ¢ï¼å¼ºè½¬ä¸º int 并èµå¼ -- éç¹ï¼ï¼ï¼ 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; }
- æ们çå°äº Integer åæ°å®é
ä¼èµ°å° OgnlOps#longValue(value) æ¹æ³ã
// 第 213 è¡ public static long longValue(Object value) throws NumberFormatException { // åæ°å¼è¥ä¸º nullï¼è¿å 0 if (value == null) return 0L; // è·ååæ°å¼å¯¹åºç±» Class c = value.getClass(); // è¥åæ°å¼ä¸ºæ°å¼ç±»åï¼å¼ºè½¬ä¸ºæ°å¼ç±»ååè¿å if (c.getSuperclass() == Number.class) return ((Number) value).longValue(); // è¥åæ°å¼ä¸ºå¸å°ç±»åï¼å¼ºè½¬ä¸ºå¸å°ç±»ååï¼è½¬ä¸º 0 æ 1 åè¿å if (c == Boolean.class) return ((Boolean) value).booleanValue() ? 1 : 0; // è¥åæ°å¼ä¸ºæ°å¼ç±»åï¼å¼ºè½¬ä¸ºåèç±»ååè¿å if (c == Character.class) return ((Character) value).charValue(); // è¥æªå¹é å°åæ°ç±»åï¼è½¬ä¸ºå符串åï¼å转为é¿æ´åç±»ååè¿å return Long.parseLong(stringValue(value, true)); /* è¿éå¯ä»¥çåºï¼ä¸ç®¡æ¯ '' è¿æ¯ 0ï¼é½ç»ä¸å 0 å¤ç */ }
ç»è®º
å¨ Mybatis ç if æ ç¾ä¸ï¼ä¸ºäºå ¼å®¹é误çç±»åä¼ åï¼å¦ï¼åæ°ä¸º Integer ç±»åï¼å´ä¼ å ¥ String ç±»åï¼ï¼å¨æ°å¼è½¬æ¢çå¤çä¸ï¼éæ°å¼ç±»åçåæ°å¼ä¼è½¬æ¢ä¸ºæ°å¼ç±»åçå¼ãç¶åéè¿å¤ææ¯å¦ä¸º 0 ï¼å³å® if æ ç¾ä¸ç sql è¯å¥æ¯å¦çæã
å æ¤ï¼å½æ们使ç¨
ageGroup != null and ageGroup != ''
æ¡ä»¶æ¶ï¼å¦æå ¥å为 0ï¼ä¼è¢« Mybatis 忽ç¥ã
ä¼åºç°ç±»ä¼¼æ åµçç±»åè¿å æ¬å ¶ä»æ°å¼ç±»åï¼DoubleãByteãCharacterãShortãLongãFloatãBigIntegerãBigDecimalã