Skip to content

Commit d5fd433

Browse files
committed
Refactoring parameter naming rule.
1 parent b93256a commit d5fd433

File tree

3 files changed

+125
-86
lines changed

3 files changed

+125
-86
lines changed

src/main/java/org/apache/ibatis/binding/MapperMethod.java

Lines changed: 4 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717

1818
import org.apache.ibatis.annotations.Flush;
1919
import org.apache.ibatis.annotations.MapKey;
20-
import org.apache.ibatis.annotations.Param;
2120
import org.apache.ibatis.cursor.Cursor;
2221
import org.apache.ibatis.mapping.MappedStatement;
2322
import org.apache.ibatis.mapping.SqlCommandType;
2423
import org.apache.ibatis.reflection.MetaObject;
24+
import org.apache.ibatis.reflection.ParamNameResolver;
2525
import org.apache.ibatis.reflection.TypeParameterResolver;
2626
import org.apache.ibatis.session.Configuration;
2727
import org.apache.ibatis.session.ResultHandler;
@@ -241,8 +241,7 @@ public static class MethodSignature {
241241
private final String mapKey;
242242
private final Integer resultHandlerIndex;
243243
private final Integer rowBoundsIndex;
244-
private final SortedMap<Integer, String> params;
245-
private final boolean hasNamedParameters;
244+
private final ParamNameResolver paramNameResolver;
246245

247246
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
248247
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
@@ -258,32 +257,13 @@ public MethodSignature(Configuration configuration, Class<?> mapperInterface, Me
258257
this.returnsCursor = Cursor.class.equals(this.returnType);
259258
this.mapKey = getMapKey(method);
260259
this.returnsMap = (this.mapKey != null);
261-
this.hasNamedParameters = hasNamedParams(method);
262260
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
263261
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
264-
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters));
262+
this.paramNameResolver = new ParamNameResolver(method);
265263
}
266264

267265
public Object convertArgsToSqlCommandParam(Object[] args) {
268-
final int paramCount = params.size();
269-
if (args == null || paramCount == 0) {
270-
return null;
271-
} else if (!hasNamedParameters && paramCount == 1) {
272-
return args[params.keySet().iterator().next()];
273-
} else {
274-
final Map<String, Object> param = new ParamMap<Object>();
275-
int i = 0;
276-
for (Map.Entry<Integer, String> entry : params.entrySet()) {
277-
param.put(entry.getValue(), args[entry.getKey()]);
278-
// issue #71, add param names as param1, param2...but ensure backward compatibility
279-
final String genericParamName = "param" + String.valueOf(i + 1);
280-
if (!param.containsKey(genericParamName)) {
281-
param.put(genericParamName, args[entry.getKey()]);
282-
}
283-
i++;
284-
}
285-
return param;
286-
}
266+
return paramNameResolver.getNamedParams(args);
287267
}
288268

289269
public boolean hasRowBounds() {
@@ -351,45 +331,6 @@ private String getMapKey(Method method) {
351331
}
352332
return mapKey;
353333
}
354-
355-
private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {
356-
final SortedMap<Integer, String> params = new TreeMap<Integer, String>();
357-
final Class<?>[] argTypes = method.getParameterTypes();
358-
for (int i = 0; i < argTypes.length; i++) {
359-
if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
360-
String paramName = String.valueOf(params.size());
361-
if (hasNamedParameters) {
362-
paramName = getParamNameFromAnnotation(method, i, paramName);
363-
}
364-
params.put(i, paramName);
365-
}
366-
}
367-
return params;
368-
}
369-
370-
private String getParamNameFromAnnotation(Method method, int i, String paramName) {
371-
final Object[] paramAnnos = method.getParameterAnnotations()[i];
372-
for (Object paramAnno : paramAnnos) {
373-
if (paramAnno instanceof Param) {
374-
paramName = ((Param) paramAnno).value();
375-
break;
376-
}
377-
}
378-
return paramName;
379-
}
380-
381-
private boolean hasNamedParams(Method method) {
382-
final Object[][] paramAnnos = method.getParameterAnnotations();
383-
for (Object[] paramAnno : paramAnnos) {
384-
for (Object aParamAnno : paramAnno) {
385-
if (aParamAnno instanceof Param) {
386-
return true;
387-
}
388-
}
389-
}
390-
return false;
391-
}
392-
393334
}
394335

395336
}

src/main/java/org/apache/ibatis/builder/annotation/ProviderSqlSource.java

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
import java.util.HashMap;
2020
import java.util.Map;
2121

22-
import org.apache.ibatis.annotations.Param;
2322
import org.apache.ibatis.builder.BuilderException;
2423
import org.apache.ibatis.builder.SqlSourceBuilder;
2524
import org.apache.ibatis.mapping.BoundSql;
2625
import org.apache.ibatis.mapping.SqlSource;
26+
import org.apache.ibatis.reflection.ParamNameResolver;
2727
import org.apache.ibatis.session.Configuration;
2828

2929
/**
@@ -53,7 +53,7 @@ public ProviderSqlSource(Configuration config, Object provider) {
5353
+ "'. Sql provider method can not overload.");
5454
}
5555
this.providerMethod = m;
56-
this.providerMethodArgumentNames = extractProviderMethodArgumentNames(m);
56+
this.providerMethodArgumentNames = new ParamNameResolver(m).getNames();
5757
}
5858
}
5959
}
@@ -105,27 +105,6 @@ private SqlSource createSqlSource(Object parameterObject) {
105105
}
106106
}
107107

108-
private String[] extractProviderMethodArgumentNames(Method providerMethod) {
109-
String[] argumentNames = new String[providerMethod.getParameterTypes().length];
110-
for (int i = 0; i < argumentNames.length; i++) {
111-
Param param = findParamAnnotation(providerMethod, i);
112-
argumentNames[i] = param != null ? param.value() : "param" + (i + 1);
113-
}
114-
return argumentNames;
115-
}
116-
117-
private Param findParamAnnotation(Method providerMethod, int parameterIndex) {
118-
final Object[] annotations = providerMethod.getParameterAnnotations()[parameterIndex];
119-
Param param = null;
120-
for (Object annotation : annotations) {
121-
if (annotation instanceof Param) {
122-
param = Param.class.cast(annotation);
123-
break;
124-
}
125-
}
126-
return param;
127-
}
128-
129108
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
130109
Object[] args = new Object[argumentNames.length];
131110
for (int i = 0; i < args.length; i++) {
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/**
2+
* Copyright 2009-2016 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+
* http://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+
17+
package org.apache.ibatis.reflection;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.Method;
21+
import java.util.Collections;
22+
import java.util.Map;
23+
import java.util.SortedMap;
24+
import java.util.TreeMap;
25+
26+
import org.apache.ibatis.annotations.Param;
27+
import org.apache.ibatis.binding.MapperMethod.ParamMap;
28+
import org.apache.ibatis.session.ResultHandler;
29+
import org.apache.ibatis.session.RowBounds;
30+
31+
public class ParamNameResolver {
32+
33+
private static final String GENERIC_NAME_PREFIX = "param";
34+
35+
/**
36+
* <p>
37+
* The key is the index and the value is the name of the parameter.<br />
38+
* The name is obtained from {@link Param} if specified. When {@link Param} is not specified,
39+
* the parameter index is used. Note that this index could be different from the actual index
40+
* when the method has special parameters (i.e. {@link RowBounds} or {@link ResultHandler}).
41+
* </p>
42+
* <ul>
43+
* <li>aMethod(@Param("M") int a, @Param("N") int b) -&gt; {{0, "M"}, {1, "N"}}</li>
44+
* <li>aMethod(int a, int b) -&gt; {{0, "0"}, {1, "1"}}</li>
45+
* <li>aMethod(int a, RowBounds rb, int b) -&gt; {{0, "0"}, {2, "1"}}</li>
46+
* </ul>
47+
*/
48+
private final SortedMap<Integer, String> names;
49+
50+
private boolean hasParamAnnotation;
51+
52+
public ParamNameResolver(Method method) {
53+
final Class<?>[] paramTypes = method.getParameterTypes();
54+
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
55+
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
56+
int paramCount = paramAnnotations.length;
57+
// get names from @Param annotations
58+
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
59+
if (isSpecialParameter(paramTypes[paramIndex])) {
60+
// skip special parameters
61+
continue;
62+
}
63+
String name = null;
64+
for (Annotation annotation : paramAnnotations[paramIndex]) {
65+
if (annotation instanceof Param) {
66+
hasParamAnnotation = true;
67+
name = ((Param) annotation).value();
68+
break;
69+
}
70+
}
71+
// When @Param is not specified, use the parameter index as the name
72+
// ("0", "1", ...) gcode issue #71
73+
map.put(paramIndex, name == null ? String.valueOf(map.size()) : name);
74+
}
75+
names = Collections.unmodifiableSortedMap(map);
76+
}
77+
78+
private static boolean isSpecialParameter(Class<?> clazz) {
79+
return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
80+
}
81+
82+
/**
83+
* Returns parameter names referenced by SQL providers.
84+
*/
85+
public String[] getNames() {
86+
return names.values().toArray(new String[0]);
87+
}
88+
89+
/**
90+
* <p>
91+
* A single non-special parameter is returned without a name.<br />
92+
* Multiple parameters are named using the naming rule.<br />
93+
* In addition to the default names, this method also adds the generic names (param1, param2,
94+
* ...).
95+
* </p>
96+
*/
97+
public Object getNamedParams(Object[] args) {
98+
final int paramCount = names.size();
99+
if (args == null || paramCount == 0) {
100+
return null;
101+
} else if (!hasParamAnnotation && paramCount == 1) {
102+
return args[names.firstKey()];
103+
} else {
104+
final Map<String, Object> param = new ParamMap<Object>();
105+
int i = 0;
106+
for (Map.Entry<Integer, String> entry : names.entrySet()) {
107+
param.put(entry.getValue(), args[entry.getKey()]);
108+
// add generic param names (param1, param2, ...)
109+
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
110+
// ensure not to overwrite parameter named with @Param
111+
if (!names.containsValue(genericParamName)) {
112+
param.put(genericParamName, args[entry.getKey()]);
113+
}
114+
i++;
115+
}
116+
return param;
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)