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

mybatis / mybatis-3 / 2681

28 Jan 2025 05:48PM UTC coverage: 87.102% (-0.1%) from 87.217%
2681

Pull #3379

github

web-flow
Merge 8a7949334 into e20272705
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class`

3815 of 4645 branches covered (82.13%)

467 of 525 new or added lines in 37 files covered. (88.95%)

26 existing lines in 5 files now uncovered.

9900 of 11366 relevant lines covered (87.1%)

0.87 hits per line

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

98.86
/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.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.reflection;
17

18
import java.lang.annotation.Annotation;
19
import java.lang.reflect.GenericArrayType;
20
import java.lang.reflect.Method;
21
import java.lang.reflect.ParameterizedType;
22
import java.lang.reflect.Type;
23
import java.util.Collection;
24
import java.util.Collections;
25
import java.util.HashMap;
26
import java.util.List;
27
import java.util.Map;
28
import java.util.Optional;
29
import java.util.SortedMap;
30
import java.util.TreeMap;
31

32
import org.apache.ibatis.annotations.Param;
33
import org.apache.ibatis.binding.MapperMethod.ParamMap;
34
import org.apache.ibatis.reflection.property.PropertyTokenizer;
35
import org.apache.ibatis.session.Configuration;
36
import org.apache.ibatis.session.ResultHandler;
37
import org.apache.ibatis.session.RowBounds;
38

39
public class ParamNameResolver {
40

41
  public static final String GENERIC_NAME_PREFIX = "param";
42

43
  public static final String[] GENERIC_NAME_CACHE = new String[10];
1✔
44

45
  static {
46
    for (int i = 0; i < 10; i++) {
1✔
47
      GENERIC_NAME_CACHE[i] = GENERIC_NAME_PREFIX + (i + 1);
1✔
48
    }
49
  }
1✔
50

51
  private final boolean useActualParamName;
52

53
  /**
54
   * The key is the index and the value is the name of the parameter.<br />
55
   * The name is obtained from {@link Param} if specified. When {@link Param} is not specified, the parameter index is
56
   * used. Note that this index could be different from the actual index when the method has special parameters (i.e.
57
   * {@link RowBounds} or {@link ResultHandler}).
58
   * <ul>
59
   * <li>aMethod(@Param("M") int a, @Param("N") int b) -&gt; {{0, "M"}, {1, "N"}}</li>
60
   * <li>aMethod(int a, int b) -&gt; {{0, "0"}, {1, "1"}}</li>
61
   * <li>aMethod(int a, RowBounds rb, int b) -&gt; {{0, "0"}, {2, "1"}}</li>
62
   * </ul>
63
   */
64
  private final SortedMap<Integer, String> names;
65
  private final Map<String, Type> typeMap = new HashMap<>();
1✔
66

67
  private boolean hasParamAnnotation;
68
  private boolean useParamMap;
69

70
  public ParamNameResolver(Configuration config, Method method, Class<?> mapperClass) {
1✔
71
    this.useActualParamName = config.isUseActualParamName();
1✔
72
    final Class<?>[] paramTypes = method.getParameterTypes();
1✔
73
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
1✔
74
    final SortedMap<Integer, String> map = new TreeMap<>();
1✔
75
    Type[] actualParamTypes = TypeParameterResolver.resolveParamTypes(method, mapperClass);
1✔
76
    int paramCount = paramAnnotations.length;
1✔
77
    // get names from @Param annotations
78
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
1✔
79
      if (isSpecialParameter(paramTypes[paramIndex])) {
1✔
80
        // skip special parameters
81
        continue;
1✔
82
      }
83
      String name = null;
1✔
84
      for (Annotation annotation : paramAnnotations[paramIndex]) {
1✔
85
        if (annotation instanceof Param) {
1!
86
          hasParamAnnotation = true;
1✔
87
          useParamMap = true;
1✔
88
          name = ((Param) annotation).value();
1✔
89
          break;
1✔
90
        }
91
      }
92
      if (name == null) {
1✔
93
        // @Param was not specified.
94
        if (useActualParamName) {
1✔
95
          name = getActualParamName(method, paramIndex);
1✔
96
        }
97
        if (name == null) {
1✔
98
          // use the parameter index as the name ("0", "1", ...)
99
          // gcode issue #71
100
          name = String.valueOf(map.size());
1✔
101
        }
102
      }
103
      map.put(paramIndex, name);
1✔
104
      typeMap.put(name, actualParamTypes[paramIndex]);
1✔
105
    }
106
    names = Collections.unmodifiableSortedMap(map);
1✔
107
    if (names.size() > 1) {
1✔
108
      useParamMap = true;
1✔
109
    }
110
    if (names.size() == 1) {
1✔
111
      Type soleParamType = actualParamTypes[0];
1✔
112
      if (soleParamType instanceof GenericArrayType) {
1!
NEW
113
        typeMap.put("array", soleParamType);
×
114
      } else {
115
        Class<?> soleParamClass = null;
1✔
116
        if (soleParamType instanceof ParameterizedType) {
1✔
117
          soleParamClass = (Class<?>) ((ParameterizedType) soleParamType).getRawType();
1✔
118
        } else if (soleParamType instanceof Class) {
1!
119
          soleParamClass = (Class<?>) soleParamType;
1✔
120
        }
121
        if (Collection.class.isAssignableFrom(soleParamClass)) {
1✔
122
          typeMap.put("collection", soleParamType);
1✔
123
          if (List.class.isAssignableFrom(soleParamClass)) {
1✔
124
            typeMap.put("list", soleParamType);
1✔
125
          }
126
        }
127
      }
128
    }
129
  }
1✔
130

131
  private String getActualParamName(Method method, int paramIndex) {
132
    return ParamNameUtil.getParamNames(method).get(paramIndex);
1✔
133
  }
134

135
  private static boolean isSpecialParameter(Class<?> clazz) {
136
    return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
1✔
137
  }
138

139
  /**
140
   * Returns parameter names referenced by SQL providers.
141
   *
142
   * @return the names
143
   */
144
  public String[] getNames() {
145
    return names.values().toArray(new String[0]);
1✔
146
  }
147

148
  /**
149
   * A single non-special parameter is returned without a name. Multiple parameters are named using the naming rule. In
150
   * addition to the default names, this method also adds the generic names (param1, param2, ...).
151
   *
152
   * @param args
153
   *          the args
154
   *
155
   * @return the named params
156
   */
157
  public Object getNamedParams(Object[] args) {
158
    final int paramCount = names.size();
1✔
159
    if (args == null || paramCount == 0) {
1✔
160
      return null;
1✔
161
    }
162
    if (!hasParamAnnotation && paramCount == 1) {
1✔
163
      Object value = args[names.firstKey()];
1✔
164
      return wrapToMapIfCollection(value, useActualParamName ? names.get(names.firstKey()) : null);
1✔
165
    } else {
166
      final Map<String, Object> param = new ParamMap<>();
1✔
167
      int i = 0;
1✔
168
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
1✔
169
        param.put(entry.getValue(), args[entry.getKey()]);
1✔
170
        // add generic param names (param1, param2, ...)
171
        final String genericParamName = i < 10 ? GENERIC_NAME_CACHE[i] : GENERIC_NAME_PREFIX + (i + 1);
1!
172
        // ensure not to overwrite parameter named with @Param
173
        if (!names.containsValue(genericParamName)) {
1!
174
          param.put(genericParamName, args[entry.getKey()]);
1✔
175
        }
176
        i++;
1✔
177
      }
1✔
178
      return param;
1✔
179
    }
180
  }
181

182
  public Type getType(String name) {
183
    PropertyTokenizer propertyTokenizer = new PropertyTokenizer(name);
1✔
184
    Type type = typeMap.get(propertyTokenizer.getName());
1✔
185
    if (propertyTokenizer.getIndex() != null) {
1✔
186
      if (type instanceof ParameterizedType) {
1✔
187
        Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
1✔
188
        return typeArgs[0];
1✔
189
      } else if (type instanceof Class && ((Class<?>) type).isArray()) {
1!
190
        return ((Class<?>) type).getComponentType();
1✔
191
      }
192
    }
193
    // TODO: param1, param2
194
    return type;
1✔
195
  }
196

197
  /**
198
   * Wrap to a {@link ParamMap} if object is {@link Collection} or array.
199
   *
200
   * @param object
201
   *          a parameter object
202
   * @param actualParamName
203
   *          an actual parameter name (If specify a name, set an object to {@link ParamMap} with specified name)
204
   *
205
   * @return a {@link ParamMap}
206
   *
207
   * @since 3.5.5
208
   */
209
  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
210
    if (object instanceof Collection) {
1✔
211
      ParamMap<Object> map = new ParamMap<>();
1✔
212
      map.put("collection", object);
1✔
213
      if (object instanceof List) {
1✔
214
        map.put("list", object);
1✔
215
      }
216
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
1✔
217
      return map;
1✔
218
    }
219
    if (object != null && object.getClass().isArray()) {
1✔
220
      ParamMap<Object> map = new ParamMap<>();
1✔
221
      map.put("array", object);
1✔
222
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
1✔
223
      return map;
1✔
224
    }
225
    return object;
1✔
226
  }
227

228
  public boolean isUseParamMap() {
229
    return useParamMap;
1✔
230
  }
231
}
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

© 2026 Coveralls, Inc