Skip to content

Commit 20a6d97

Browse files
committed
Support to assign single primitive parameter on SQL provider method
See mybatisgh-1595
1 parent e003931 commit 20a6d97

File tree

4 files changed

+97
-21
lines changed

4 files changed

+97
-21
lines changed

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

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ProviderSqlSource implements SqlSource {
3838
private final Configuration configuration;
3939
private final Class<?> providerType;
4040
private final LanguageDriver languageDriver;
41+
private final Method mapperMethod;
4142
private Method providerMethod;
4243
private String[] providerMethodArgumentNames;
4344
private Class<?>[] providerMethodParameterTypes;
@@ -59,6 +60,7 @@ public ProviderSqlSource(Configuration configuration, Object provider, Class<?>
5960
String providerMethodName;
6061
try {
6162
this.configuration = configuration;
63+
this.mapperMethod = mapperMethod;
6264
Lang lang = mapperMethod == null ? null : mapperMethod.getAnnotation(Lang.class);
6365
this.languageDriver = configuration.getLanguageDriver(lang == null ? null : lang.value());
6466
this.providerType = getProviderType(provider, mapperMethod);
@@ -116,33 +118,45 @@ private SqlSource createSqlSource(Object parameterObject) {
116118
try {
117119
int bindParameterCount = providerMethodParameterTypes.length - (providerContext == null ? 0 : 1);
118120
String sql;
119-
if (providerMethodParameterTypes.length == 0) {
121+
if (parameterObject instanceof Map) {
122+
if (bindParameterCount == 1 && providerMethodParameterTypes[0] == Map.class) {
123+
sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
124+
} else {
125+
@SuppressWarnings("unchecked")
126+
Map<String, Object> params = (Map<String, Object>) parameterObject;
127+
sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
128+
}
129+
} else if (providerMethodParameterTypes.length == 0) {
120130
sql = invokeProviderMethod();
121-
} else if (bindParameterCount == 0) {
122-
sql = invokeProviderMethod(providerContext);
123-
} else if (bindParameterCount == 1
124-
&& (parameterObject == null || providerMethodParameterTypes[providerContextIndex == null || providerContextIndex == 1 ? 0 : 1].isAssignableFrom(parameterObject.getClass()))) {
131+
} else if (providerMethodParameterTypes.length == 1) {
132+
if (providerContext == null) {
133+
sql = invokeProviderMethod(parameterObject);
134+
} else {
135+
sql = invokeProviderMethod(providerContext);
136+
}
137+
} else if (providerMethodParameterTypes.length == 2) {
125138
sql = invokeProviderMethod(extractProviderMethodArguments(parameterObject));
126-
} else if (parameterObject instanceof Map) {
127-
@SuppressWarnings("unchecked")
128-
Map<String, Object> params = (Map<String, Object>) parameterObject;
129-
sql = invokeProviderMethod(extractProviderMethodArguments(params, providerMethodArgumentNames));
130139
} else {
131-
throw new BuilderException("Error invoking SqlProvider method ("
132-
+ providerType.getName() + "." + providerMethod.getName()
133-
+ "). Cannot invoke a method that holds "
134-
+ (bindParameterCount == 1 ? "named argument(@Param)" : "multiple arguments")
135-
+ " using a specifying parameterObject. In this case, please specify a 'java.util.Map' object.");
140+
throw new BuilderException("Cannot invoke SqlProvider method '" + providerMethod
141+
+ "' with specify parameter '" + (parameterObject == null ? null : parameterObject.getClass())
142+
+ "' because SqlProvider method arguments for '" + mapperMethod + "' is an invalid combination.");
136143
}
137144
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
138145
return languageDriver.createSqlSource(configuration, sql, parameterType);
139146
} catch (BuilderException e) {
140147
throw e;
141148
} catch (Exception e) {
142-
throw new BuilderException("Error invoking SqlProvider method ("
143-
+ providerType.getName() + "." + providerMethod.getName()
144-
+ "). Cause: " + e, e);
149+
throw new BuilderException("Error invoking SqlProvider method '" + providerMethod
150+
+ "' with specify parameter '" + (parameterObject == null ? null : parameterObject.getClass()) + "'. Cause: " + extractRootCause(e), e);
151+
}
152+
}
153+
154+
private Throwable extractRootCause(Exception e) {
155+
Throwable cause = e;
156+
while(cause.getCause() != null) {
157+
cause = e.getCause();
145158
}
159+
return cause;
146160
}
147161

148162
private Object[] extractProviderMethodArguments(Object parameterObject) {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2009-2018 the original author or authors.
2+
* Copyright 2009-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,9 @@ public interface Mapper extends BaseMapper<User> {
4141
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByCriteriaMapQuery")
4242
List<User> getUsersByCriteriaMap(Map<String, Object> criteria);
4343

44+
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByCriteriaMapWithParamQuery")
45+
List<User> getUsersByCriteriaMapWithParam(Map<String, Object> criteria);
46+
4447
@SelectProvider(type = OurSqlBuilder.class, method = "buildGetUsersByNameQuery")
4548
List<User> getUsersByName(String name, String orderByColumn);
4649

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ public String buildGetUsersByCriteriaMapQuery(final Map<String, Object> criteria
8484
}}.toString();
8585
}
8686

87+
public String buildGetUsersByCriteriaMapWithParamQuery(@Param("id") Integer id, @Param("name") String name) {
88+
return new SQL() {{
89+
SELECT("*");
90+
FROM("users");
91+
if (id != null) {
92+
WHERE("id = #{id}");
93+
}
94+
if (name != null) {
95+
WHERE("name like #{name} || '%'");
96+
}
97+
}}.toString();
98+
}
99+
87100
public String buildGetUsersByNameQuery(final String name, final String orderByColumn) {
88101
return new SQL(){{
89102
SELECT("*");

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

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,30 @@ void shouldGetUsersByCriteriaMap() {
165165
}
166166
}
167167

168+
@Test
169+
void shouldGetUsersByCriteriaMapWithParam() {
170+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
171+
Mapper mapper = sqlSession.getMapper(Mapper.class);
172+
{
173+
Map<String, Object> criteria = new HashMap<>();
174+
criteria.put("id", 1);
175+
List<User> users = mapper.getUsersByCriteriaMapWithParam(criteria);
176+
assertEquals(1, users.size());
177+
assertEquals("User1", users.get(0).getName());
178+
}
179+
{
180+
Map<String, Object> criteria = new HashMap<>();
181+
criteria.put("name", "User");
182+
List<User> users = mapper.getUsersByCriteriaMapWithParam(criteria);
183+
assertEquals(4, users.size());
184+
assertEquals("User1", users.get(0).getName());
185+
assertEquals("User2", users.get(1).getName());
186+
assertEquals("User3", users.get(2).getName());
187+
assertEquals("User4", users.get(3).getName());
188+
}
189+
}
190+
}
191+
168192
// Test for multiple parameter without @Param
169193
@Test
170194
void shouldGetUsersByName() {
@@ -330,7 +354,7 @@ void notSupportParameterObjectOnMultipleArguments() throws NoSuchMethodException
330354
.getBoundSql(new Object());
331355
fail();
332356
} catch (BuilderException e) {
333-
assertTrue(e.getMessage().contains("Error invoking SqlProvider method (org.apache.ibatis.submitted.sqlprovider.OurSqlBuilder.buildGetUsersByNameQuery). Cannot invoke a method that holds multiple arguments using a specifying parameterObject. In this case, please specify a 'java.util.Map' object."));
357+
assertTrue(e.getMessage().contains("Error invoking SqlProvider method 'public java.lang.String org.apache.ibatis.submitted.sqlprovider.OurSqlBuilder.buildGetUsersByNameQuery(java.lang.String,java.lang.String)' with specify parameter 'class java.lang.Object'. Cause: java.lang.IllegalArgumentException: wrong number of arguments"));
334358
}
335359
}
336360

@@ -344,7 +368,7 @@ void notSupportParameterObjectOnNamedArgument() throws NoSuchMethodException {
344368
.getBoundSql(new Object());
345369
fail();
346370
} catch (BuilderException e) {
347-
assertTrue(e.getMessage().contains("Error invoking SqlProvider method (org.apache.ibatis.submitted.sqlprovider.OurSqlBuilder.buildGetUsersByNameWithParamNameQuery). Cannot invoke a method that holds named argument(@Param) using a specifying parameterObject. In this case, please specify a 'java.util.Map' object."));
371+
assertTrue(e.getMessage().contains("Error invoking SqlProvider method 'public java.lang.String org.apache.ibatis.submitted.sqlprovider.OurSqlBuilder.buildGetUsersByNameWithParamNameQuery(java.lang.String)' with specify parameter 'class java.lang.Object'. Cause: java.lang.IllegalArgumentException: argument type mismatch"));
348372
}
349373
}
350374

@@ -358,7 +382,21 @@ void invokeError() throws NoSuchMethodException {
358382
.getBoundSql(new Object());
359383
fail();
360384
} catch (BuilderException e) {
361-
assertTrue(e.getMessage().contains("Error invoking SqlProvider method (org.apache.ibatis.submitted.sqlprovider.SqlProviderTest$ErrorSqlBuilder.invokeError). Cause: java.lang.reflect.InvocationTargetException"));
385+
assertTrue(e.getMessage().contains("Error invoking SqlProvider method 'public java.lang.String org.apache.ibatis.submitted.sqlprovider.SqlProviderTest$ErrorSqlBuilder.invokeError()' with specify parameter 'class java.lang.Object'. Cause: java.lang.UnsupportedOperationException: invokeError"));
386+
}
387+
}
388+
389+
@Test
390+
void invalidArgumentsCombination() throws NoSuchMethodException {
391+
try {
392+
Class<?> mapperType = ErrorMapper.class;
393+
Method mapperMethod = mapperType.getMethod("invalidArgumentsCombination", String.class);
394+
new ProviderSqlSource(new Configuration(),
395+
mapperMethod.getAnnotation(DeleteProvider.class), mapperType, mapperMethod)
396+
.getBoundSql("foo");
397+
fail();
398+
} catch (BuilderException e) {
399+
assertTrue(e.getMessage().contains("Cannot invoke SqlProvider method 'public java.lang.String org.apache.ibatis.submitted.sqlprovider.SqlProviderTest$ErrorSqlBuilder.invalidArgumentsCombination(org.apache.ibatis.builder.annotation.ProviderContext,java.lang.String,java.lang.String)' with specify parameter 'class java.lang.String' because SqlProvider method arguments for 'public abstract void org.apache.ibatis.submitted.sqlprovider.SqlProviderTest$ErrorMapper.invalidArgumentsCombination(java.lang.String)' is an invalid combination."));
362400
}
363401
}
364402

@@ -509,6 +547,10 @@ public interface ErrorMapper {
509547

510548
@DeleteProvider(value = String.class, type = Integer.class)
511549
void differentTypeAndValue();
550+
551+
@DeleteProvider(type = ErrorSqlBuilder.class, method = "invalidArgumentsCombination")
552+
void invalidArgumentsCombination(String value);
553+
512554
}
513555

514556
@SuppressWarnings("unused")
@@ -532,6 +574,10 @@ public String invokeError() {
532574
public String multipleProviderContext(ProviderContext providerContext1, ProviderContext providerContext2) {
533575
throw new UnsupportedOperationException("multipleProviderContext");
534576
}
577+
578+
public String invalidArgumentsCombination(ProviderContext providerContext, String value, String unnecessaryArgument) {
579+
return "";
580+
}
535581
}
536582

537583
public interface StaticMethodSqlProviderMapper {

0 commit comments

Comments
 (0)