Skip to content

Commit 4aa737f

Browse files
committed
Add constructor that pass an Annotation type instead of Object on ProviderSqlSource
The motivation for this change is to resolve issues that prevent GraalVM support. See mybatisgh-1552
1 parent 6cea6e1 commit 4aa737f

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

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

+28-7
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
import java.lang.reflect.InvocationTargetException;
2020
import java.lang.reflect.Method;
2121
import java.lang.reflect.Modifier;
22+
import java.lang.reflect.Proxy;
2223
import java.util.Map;
2324

2425
import org.apache.ibatis.annotations.Lang;
26+
import org.apache.ibatis.annotations.SelectProvider;
2527
import org.apache.ibatis.builder.BuilderException;
2628
import org.apache.ibatis.mapping.BoundSql;
2729
import org.apache.ibatis.mapping.SqlSource;
@@ -46,7 +48,8 @@ public class ProviderSqlSource implements SqlSource {
4648
private Integer providerContextIndex;
4749

4850
/**
49-
* @deprecated Please use the {@link #ProviderSqlSource(Configuration, Object, Class, Method)} instead of this.
51+
* @deprecated Since 3.5.3, Please use the {@link #ProviderSqlSource(Configuration, Annotation, Class, Method)} instead of this.
52+
* This constructor will remove at a future version.
5053
*/
5154
@Deprecated
5255
public ProviderSqlSource(Configuration configuration, Object provider) {
@@ -55,16 +58,34 @@ public ProviderSqlSource(Configuration configuration, Object provider) {
5558

5659
/**
5760
* @since 3.4.5
61+
* @deprecated Since 3.5.3, Please use the {@link #ProviderSqlSource(Configuration, Annotation, Class, Method)} instead of this.
62+
* This constructor will remove at a future version.
5863
*/
64+
@Deprecated
5965
public ProviderSqlSource(Configuration configuration, Object provider, Class<?> mapperType, Method mapperMethod) {
66+
this(configuration, provider instanceof Annotation ? (Annotation) provider :
67+
(Annotation) Proxy.newProxyInstance(SelectProvider.class.getClassLoader(), new Class<?>[]{SelectProvider.class},
68+
(proxy, method, args) -> {
69+
if (method.getName().equals("annotationType")) {
70+
return SelectProvider.class;
71+
}
72+
return provider.getClass().getMethod(method.getName()).invoke(provider);
73+
}),
74+
mapperType, mapperMethod);
75+
}
76+
77+
/**
78+
* @since 3.5.3
79+
*/
80+
public ProviderSqlSource(Configuration configuration, Annotation provider, Class<?> mapperType, Method mapperMethod) {
6081
String providerMethodName;
6182
try {
6283
this.configuration = configuration;
6384
this.mapperMethod = mapperMethod;
6485
Lang lang = mapperMethod == null ? null : mapperMethod.getAnnotation(Lang.class);
6586
this.languageDriver = configuration.getLanguageDriver(lang == null ? null : lang.value());
6687
this.providerType = getProviderType(provider, mapperMethod);
67-
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
88+
providerMethodName = (String) provider.annotationType().getMethod("method").invoke(provider);
6889

6990
if (providerMethodName.length() == 0 && ProviderMethodResolver.class.isAssignableFrom(this.providerType)) {
7091
this.providerMethod = ((ProviderMethodResolver) this.providerType.getDeclaredConstructor().newInstance())
@@ -192,18 +213,18 @@ private String invokeProviderMethod(Object... args) throws Exception {
192213
return sql != null ? sql.toString() : null;
193214
}
194215

195-
private Class<?> getProviderType(Object providerAnnotation, Method mapperMethod)
216+
private Class<?> getProviderType(Annotation providerAnnotation, Method mapperMethod)
196217
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
197-
Class<?> type = (Class<?>) providerAnnotation.getClass().getMethod("type").invoke(providerAnnotation);
198-
Class<?> value = (Class<?>) providerAnnotation.getClass().getMethod("value").invoke(providerAnnotation);
218+
Class<?> type = (Class<?>) providerAnnotation.annotationType().getMethod("type").invoke(providerAnnotation);
219+
Class<?> value = (Class<?>) providerAnnotation.annotationType().getMethod("value").invoke(providerAnnotation);
199220
if (value == void.class && type == void.class) {
200221
throw new BuilderException("Please specify either 'value' or 'type' attribute of @"
201-
+ ((Annotation) providerAnnotation).annotationType().getSimpleName()
222+
+ providerAnnotation.annotationType().getSimpleName()
202223
+ " at the '" + mapperMethod.toString() + "'.");
203224
}
204225
if (value != void.class && type != void.class && value != type) {
205226
throw new BuilderException("Cannot specify different class on 'value' and 'type' attribute of @"
206-
+ ((Annotation) providerAnnotation).annotationType().getSimpleName()
227+
+ providerAnnotation.annotationType().getSimpleName()
207228
+ " at the '" + mapperMethod.toString() + "'.");
208229
}
209230
return value == void.class ? type : value;

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

+38-1
Original file line numberDiff line numberDiff line change
@@ -298,12 +298,14 @@ void methodOverload() throws NoSuchMethodException {
298298
}
299299

300300
@Test
301+
@SuppressWarnings("deprecation")
301302
void notSqlProvider() {
302303
try {
303304
new ProviderSqlSource(new Configuration(), new Object(), null, null);
304305
fail();
305306
} catch (BuilderException e) {
306-
assertTrue(e.getMessage().contains("Error creating SqlSource for SqlProvider. Cause: java.lang.NoSuchMethodException: java.lang.Object.type()"));
307+
assertTrue(e.getMessage().contains("Error creating SqlSource for SqlProvider."));
308+
assertTrue(e.getCause().getCause().getCause().getMessage().contains("java.lang.Object.type()"));
307309
}
308310
}
309311

@@ -648,6 +650,41 @@ void providerContextAndMap() {
648650
}
649651
}
650652

653+
@Test
654+
@SuppressWarnings("deprecation")
655+
void keepBackwardCompatibilityOnDeprecatedConstructor() throws NoSuchMethodException {
656+
Class<?> mapperType = StaticMethodSqlProviderMapper.class;
657+
Method mapperMethod = mapperType.getMethod("noArgument");
658+
ProviderSqlSource sqlSource = new ProviderSqlSource(new Configuration(), new SqlProviderConfig(), mapperType, mapperMethod);
659+
assertEquals("SELECT 1", sqlSource.getBoundSql(null).getSql());
660+
}
661+
662+
@Test
663+
@SuppressWarnings("deprecation")
664+
void keepBackwardCompatibilityOnDeprecatedConstructorWithAnnotation() throws NoSuchMethodException {
665+
Class<?> mapperType = StaticMethodSqlProviderMapper.class;
666+
Method mapperMethod = mapperType.getMethod("noArgument");
667+
ProviderSqlSource sqlSource = new ProviderSqlSource(new Configuration(), (Object)mapperMethod.getAnnotation(SelectProvider.class), mapperType, mapperMethod);
668+
assertEquals("SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS", sqlSource.getBoundSql(null).getSql());
669+
}
670+
671+
public static class SqlProviderConfig {
672+
public Class<?> type() {
673+
return SqlProvider.class;
674+
}
675+
public Class<?> value() {
676+
return void.class;
677+
}
678+
public String method() {
679+
return "provideSql";
680+
}
681+
public static class SqlProvider {
682+
public static String provideSql() {
683+
return "SELECT 1";
684+
}
685+
}
686+
}
687+
651688
public interface ErrorMapper {
652689
@SelectProvider(type = ErrorSqlBuilder.class, method = "methodNotFound")
653690
void methodNotFound();

0 commit comments

Comments
 (0)