Skip to content

Commit 2beff6b

Browse files
committed
Merge pull request #639 from kazuki43zoo/support-multiple-arguments-at-sqlprovider
Allow multiple arguments at a Sql Provider method
2 parents 2410192 + 96a270a commit 2beff6b

File tree

4 files changed

+383
-6
lines changed

4 files changed

+383
-6
lines changed

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

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717

1818
import java.lang.reflect.Method;
1919
import java.util.HashMap;
20+
import java.util.Map;
2021

22+
import org.apache.ibatis.annotations.Param;
2123
import org.apache.ibatis.builder.BuilderException;
2224
import org.apache.ibatis.builder.SqlSourceBuilder;
2325
import org.apache.ibatis.mapping.BoundSql;
@@ -26,13 +28,14 @@
2628

2729
/**
2830
* @author Clinton Begin
31+
* @author Kazuki Shimizu
2932
*/
3033
public class ProviderSqlSource implements SqlSource {
3134

3235
private SqlSourceBuilder sqlSourceParser;
3336
private Class<?> providerType;
3437
private Method providerMethod;
35-
private boolean providerTakesParameterObject;
38+
private String[] providerMethodArgumentNames;
3639

3740
public ProviderSqlSource(Configuration config, Object provider) {
3841
String providerMethodName = null;
@@ -43,13 +46,19 @@ public ProviderSqlSource(Configuration config, Object provider) {
4346

4447
for (Method m : this.providerType.getMethods()) {
4548
if (providerMethodName.equals(m.getName())) {
46-
if (m.getParameterTypes().length < 2
47-
&& m.getReturnType() == String.class) {
49+
if (m.getReturnType() == String.class) {
50+
if (providerMethod != null){
51+
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
52+
+ providerMethodName + "' is found multiple in SqlProvider '" + this.providerType.getName()
53+
+ "'. Sql provider method can not overload.");
54+
}
4855
this.providerMethod = m;
49-
this.providerTakesParameterObject = m.getParameterTypes().length == 1;
56+
this.providerMethodArgumentNames = extractProviderMethodArgumentNames(m);
5057
}
5158
}
5259
}
60+
} catch (BuilderException e) {
61+
throw e;
5362
} catch (Exception e) {
5463
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
5564
}
@@ -67,19 +76,59 @@ public BoundSql getBoundSql(Object parameterObject) {
6776

6877
private SqlSource createSqlSource(Object parameterObject) {
6978
try {
79+
Class<?>[] parameterTypes = providerMethod.getParameterTypes();
7080
String sql;
71-
if (providerTakesParameterObject) {
81+
if (parameterTypes.length == 0) {
82+
sql = (String) providerMethod.invoke(providerType.newInstance());
83+
} else if (parameterTypes.length == 1) {
7284
sql = (String) providerMethod.invoke(providerType.newInstance(), parameterObject);
85+
} else if (parameterObject instanceof Map) {
86+
@SuppressWarnings("unchecked")
87+
Map<String, Object> params = (Map<String, Object>) parameterObject;
88+
sql = (String) providerMethod.invoke(providerType.newInstance(), extractProviderMethodArguments(params, providerMethodArgumentNames));
7389
} else {
74-
sql = (String) providerMethod.invoke(providerType.newInstance());
90+
throw new BuilderException("Error invoking SqlProvider method ("
91+
+ providerType.getName() + "." + providerMethod.getName()
92+
+ "). Cannot invoke a method that holds multiple arguments using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
7593
}
7694
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
7795
return sqlSourceParser.parse(sql, parameterType, new HashMap<String, Object>());
96+
} catch (BuilderException e) {
97+
throw e;
7898
} catch (Exception e) {
7999
throw new BuilderException("Error invoking SqlProvider method ("
80100
+ providerType.getName() + "." + providerMethod.getName()
81101
+ "). Cause: " + e, e);
82102
}
83103
}
84104

105+
private String[] extractProviderMethodArgumentNames(Method providerMethod) {
106+
String[] argumentNames = new String[providerMethod.getParameterTypes().length];
107+
for (int i = 0; i < argumentNames.length; i++) {
108+
Param param = findParamAnnotation(providerMethod, i);
109+
argumentNames[i] = param != null ? param.value() : "param" + (i + 1);
110+
}
111+
return argumentNames;
112+
}
113+
114+
private Param findParamAnnotation(Method providerMethod, int parameterIndex) {
115+
final Object[] annotations = providerMethod.getParameterAnnotations()[parameterIndex];
116+
Param param = null;
117+
for (Object annotation : annotations) {
118+
if (annotation instanceof Param) {
119+
param = Param.class.cast(annotation);
120+
break;
121+
}
122+
}
123+
return param;
124+
}
125+
126+
private Object[] extractProviderMethodArguments(Map<String, Object> params, String[] argumentNames) {
127+
Object[] args = new Object[argumentNames.length];
128+
for (int i = 0; i < args.length; i++) {
129+
args[i] = params.get(argumentNames[i]);
130+
}
131+
return args;
132+
}
133+
85134
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/Mapper.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
package org.apache.ibatis.submitted.sqlprovider;
1717

1818
import java.util.List;
19+
import java.util.Map;
1920

21+
import org.apache.ibatis.annotations.Param;
2022
import org.apache.ibatis.annotations.SelectProvider;
2123

2224
public interface Mapper {
@@ -25,4 +27,23 @@ public interface Mapper {
2527

2628
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUserQuery")
2729
User getUser(Integer userId);
30+
31+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetAllUsersQuery")
32+
List<User> getAllUsers();
33+
34+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByCriteriaQuery")
35+
List<User> getUsersByCriteria(User criteria);
36+
37+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByCriteriaMapQuery")
38+
List<User> getUsersByCriteriaMap(Map<String, Object> criteria);
39+
40+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByNameQuery")
41+
List<User> getUsersByName(String name, String orderByColumn);
42+
43+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByNameUsingMap")
44+
List<User> getUsersByNameUsingMap(String name, String orderByColumn);
45+
46+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByNameWithParamNameQuery")
47+
List<User> getUsersByNameWithParamName(@Param("name") String name, @Param("orderByColumn") String orderByColumn);
48+
2849
}

src/test/java/org/apache/ibatis/submitted/sqlprovider/OurSqlBuilder.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package org.apache.ibatis.submitted.sqlprovider;
1717

18+
import org.apache.ibatis.annotations.Param;
19+
import org.apache.ibatis.jdbc.SQL;
20+
1821
import java.util.List;
1922
import java.util.Map;
2023

@@ -44,4 +47,82 @@ public String buildGetUserQuery(Integer parameter) {
4447
// so it is passed as is from the mapper
4548
return "select * from users where id = #{value}";
4649
}
50+
public String buildGetAllUsersQuery() {
51+
return "select * from users order by id";
52+
}
53+
54+
public String buildGetUsersByCriteriaQuery(final User criteria) {
55+
return new SQL(){{
56+
SELECT("*");
57+
FROM("users");
58+
if (criteria.getId() != null) {
59+
WHERE("id = #{id}");
60+
}
61+
if (criteria.getName() != null) {
62+
WHERE("name like #{name} || '%'");
63+
}
64+
}}.toString();
65+
}
66+
67+
public String buildGetUsersByCriteriaMapQuery(final Map<String, Object> criteria) {
68+
return new SQL(){{
69+
SELECT("*");
70+
FROM("users");
71+
if (criteria.get("id") != null) {
72+
WHERE("id = #{id}");
73+
}
74+
if (criteria.get("name") != null) {
75+
WHERE("name like #{name} || '%'");
76+
}
77+
}}.toString();
78+
}
79+
80+
public String buildGetUsersByNameQuery(final String name, final String orderByColumn) {
81+
return new SQL(){{
82+
SELECT("*");
83+
FROM("users");
84+
if (name != null) {
85+
WHERE("name like #{param1} || '%'");
86+
}
87+
ORDER_BY(orderByColumn);
88+
}}.toString();
89+
}
90+
91+
public String buildGetUsersByNameUsingMap(Map<String, Object> params) {
92+
final String name = String.class.cast(params.get("param1"));
93+
final String orderByColumn = String.class.cast(params.get("param2"));
94+
return new SQL(){{
95+
SELECT("*");
96+
FROM("users");
97+
if (name != null) {
98+
WHERE("name like #{param1} || '%'");
99+
}
100+
ORDER_BY(orderByColumn);
101+
}}.toString();
102+
}
103+
104+
public String buildGetUsersByNameWithParamNameQuery(@Param("orderByColumn") final String orderByColumn, @Param("name") final String name) {
105+
return new SQL(){{
106+
SELECT("*");
107+
FROM("users");
108+
if (name != null) {
109+
WHERE("name like #{name} || '%'");
110+
}
111+
ORDER_BY(orderByColumn);
112+
}}.toString();
113+
}
114+
115+
public String buildGetUsersByNameWithParamNameQueryUsingMap(Map<String, Object> params) {
116+
final String name = String.class.cast(params.get("name"));
117+
final String orderByColumn = String.class.cast(params.get("orderByColumn"));
118+
return new SQL(){{
119+
SELECT("*");
120+
FROM("users");
121+
if (name != null) {
122+
WHERE("name like #{param1} || '%'");
123+
}
124+
ORDER_BY(orderByColumn);
125+
}}.toString();
126+
}
127+
47128
}

0 commit comments

Comments
 (0)