• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / mybatis-3 / 2686

01 Feb 2025 09:55PM UTC coverage: 87.093% (-0.1%) from 87.217%
2686

Pull #3379

github

web-flow
Merge c97c5c598 into 3d71c862a
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class` and respect runtime JDBC type

3825 of 4663 branches covered (82.03%)

515 of 579 new or added lines in 36 files covered. (88.95%)

28 existing lines in 6 files now uncovered.

9912 of 11381 relevant lines covered (87.09%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.73
/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java
1
/*
2
 *    Copyright 2009-2025 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.apache.ibatis.builder;
17

18
import java.lang.reflect.Type;
19
import java.sql.ResultSet;
20
import java.util.List;
21
import java.util.Map;
22
import java.util.Map.Entry;
23

24
import org.apache.ibatis.binding.MapperMethod.ParamMap;
25
import org.apache.ibatis.mapping.ParameterMapping;
26
import org.apache.ibatis.mapping.ParameterMode;
27
import org.apache.ibatis.parsing.TokenHandler;
28
import org.apache.ibatis.reflection.MetaClass;
29
import org.apache.ibatis.reflection.MetaObject;
30
import org.apache.ibatis.reflection.ParamNameResolver;
31
import org.apache.ibatis.reflection.property.PropertyTokenizer;
32
import org.apache.ibatis.session.Configuration;
33
import org.apache.ibatis.type.JdbcType;
34
import org.apache.ibatis.type.TypeHandler;
35

36
public class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
37

38
  private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName";
39
  private final List<ParameterMapping> parameterMappings;
40
  private final Class<?> parameterType;
41
  private final MetaObject metaParameters;
42
  private final Object parameterObject;
43
  private final boolean paramExists;
44
  private final ParamNameResolver paramNameResolver;
45

46
  private Type genericType = null;
1✔
47
  private TypeHandler<?> typeHandler = null;
1✔
48

49
  public ParameterMappingTokenHandler(List<ParameterMapping> parameterMappings, Configuration configuration,
50
      Object parameterObject, Class<?> parameterType, Map<String, Object> additionalParameters,
51
      ParamNameResolver paramNameResolver, boolean paramExists) {
52
    super(configuration);
1✔
53
    this.parameterType = parameterObject == null ? (parameterType == null ? Object.class : parameterType)
1✔
54
        : parameterObject.getClass();
1✔
55
    this.metaParameters = configuration.newMetaObject(additionalParameters);
1✔
56
    this.parameterObject = parameterObject;
1✔
57
    this.paramExists = paramExists;
1✔
58
    this.parameterMappings = parameterMappings;
1✔
59
    this.paramNameResolver = paramNameResolver;
1✔
60
  }
1✔
61

62
  public ParameterMappingTokenHandler(List<ParameterMapping> parameterMappings, Configuration configuration,
63
      Class<?> parameterType, Map<String, Object> additionalParameters, ParamNameResolver paramNameResolver) {
64
    super(configuration);
1✔
65
    this.parameterType = parameterType;
1✔
66
    this.metaParameters = configuration.newMetaObject(additionalParameters);
1✔
67
    this.parameterObject = null;
1✔
68
    this.paramExists = false;
1✔
69
    this.parameterMappings = parameterMappings;
1✔
70
    this.paramNameResolver = paramNameResolver;
1✔
71
  }
1✔
72

73
  public List<ParameterMapping> getParameterMappings() {
74
    return parameterMappings;
1✔
75
  }
76

77
  @Override
78
  public String handleToken(String content) {
79
    parameterMappings.add(buildParameterMapping(content));
1✔
80
    return "?";
1✔
81
  }
82

83
  private ParameterMapping buildParameterMapping(String content) {
84
    Map<String, String> propertiesMap = parseParameterMapping(content);
1✔
85

86
    final String property = propertiesMap.remove("property");
1✔
87
    final JdbcType jdbcType = resolveJdbcType(propertiesMap.remove("jdbcType"));
1✔
88
    final String typeHandlerAlias = propertiesMap.remove("typeHandler");
1✔
89

90
    ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, (Class<?>) null);
1✔
91
    PropertyTokenizer propertyTokenizer = new PropertyTokenizer(property);
1✔
92
    builder.jdbcType(jdbcType);
1✔
93
    final Class<?> javaType = figureOutJavaType(propertiesMap, property, propertyTokenizer, jdbcType);
1✔
94
    builder.javaType(javaType);
1✔
95
    if (genericType == null) {
1✔
96
      genericType = javaType;
1✔
97
    }
98
    if ((typeHandler == null || typeHandlerAlias != null) && genericType != null && genericType != Object.class) {
1!
99
      typeHandler = resolveTypeHandler(parameterType, property, genericType, jdbcType, typeHandlerAlias);
1✔
100
    }
101
    builder.typeHandler(typeHandler);
1✔
102

103
    ParameterMode mode = null;
1✔
104
    for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
1✔
105
      String name = entry.getKey();
1✔
106
      String value = entry.getValue();
1✔
107
      if ("mode".equals(name)) {
1!
108
        mode = resolveParameterMode(value);
1✔
109
        builder.mode(mode);
1✔
UNCOV
110
      } else if ("numericScale".equals(name)) {
×
111
        builder.numericScale(Integer.valueOf(value));
×
UNCOV
112
      } else if ("resultMap".equals(name)) {
×
113
        builder.resultMapId(value);
×
UNCOV
114
      } else if ("jdbcTypeName".equals(name)) {
×
UNCOV
115
        builder.jdbcTypeName(value);
×
UNCOV
116
      } else if ("expression".equals(name)) {
×
UNCOV
117
        throw new BuilderException("Expression based parameters are not supported yet");
×
118
      } else {
119
        throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content
×
120
            + "}.  Valid properties are " + PARAMETER_PROPERTIES);
121
      }
122
    }
1✔
123
    if (!ParameterMode.OUT.equals(mode) && paramExists) {
1✔
124
      if (metaParameters.hasGetter(propertyTokenizer.getName())) {
1✔
125
        builder.value(metaParameters.getValue(property));
1✔
126
      } else if (parameterObject == null) {
1!
127
        builder.value(null);
×
128
      } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
1✔
129
        builder.value(parameterObject);
1✔
130
      } else {
131
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
1✔
132
        builder.value(metaObject.getValue(property));
1✔
133
      }
134
    }
135
    return builder.build();
1✔
136
  }
137

138
  private Class<?> figureOutJavaType(Map<String, String> propertiesMap, String property,
139
      PropertyTokenizer propertyTokenizer, JdbcType jdbcType) {
140
    Class<?> javaType = resolveClass(propertiesMap.remove("javaType"));
1✔
141
    if (javaType != null) {
1✔
142
      return javaType;
1✔
143
    }
144
    if (metaParameters.hasGetter(propertyTokenizer.getName())) { // issue #448 get type from additional params
1✔
145
      return metaParameters.getGetterType(property);
1✔
146
    }
147
    typeHandler = resolveTypeHandler(parameterType, null, null, jdbcType, (Class<? extends TypeHandler<?>>) null);
1✔
148
    if (typeHandler != null) {
1✔
149
      return parameterType;
1✔
150
    }
151
    if (JdbcType.CURSOR.equals(jdbcType)) {
1!
NEW
152
      return ResultSet.class;
×
153
    }
154
    if (paramNameResolver != null && ParamMap.class.equals(parameterType)) {
1✔
155
      Type actualParamType = paramNameResolver.getType(property);
1✔
156
      if (actualParamType instanceof Type) {
1✔
157
        MetaClass metaClass = MetaClass.forClass(actualParamType, configuration.getReflectorFactory());
1✔
158
        String multiParamsPropertyName;
159
        if (propertyTokenizer.hasNext()) {
1✔
160
          multiParamsPropertyName = propertyTokenizer.getChildren();
1✔
161
          if (metaClass.hasGetter(multiParamsPropertyName)) {
1✔
162
            Entry<Type, Class<?>> getterType = metaClass.getGenericGetterType(multiParamsPropertyName);
1✔
163
            genericType = getterType.getKey();
1✔
164
            return getterType.getValue();
1✔
165
          }
166
        } else {
167
          genericType = actualParamType;
1✔
168
        }
169
      }
170
      return Object.class;
1✔
171
    }
172
    if (Map.class.isAssignableFrom(parameterType)) {
1✔
173
      return Object.class;
1✔
174
    }
175
    MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
1✔
176
    if (metaClass.hasGetter(property)) {
1✔
177
      Entry<Type, Class<?>> getterType = metaClass.getGenericGetterType(property);
1✔
178
      genericType = getterType.getKey();
1✔
179
      return getterType.getValue();
1✔
180
    }
181
    return Object.class;
1✔
182
  }
183

184
  private Map<String, String> parseParameterMapping(String content) {
185
    try {
186
      return new ParameterExpression(content);
1✔
187
    } catch (BuilderException ex) {
×
188
      throw ex;
×
189
    } catch (Exception ex) {
1✔
190
      throw new BuilderException("Parsing error was found in mapping #{" + content
1✔
191
          + "}.  Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex);
192
    }
193
  }
194
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc