1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.submitted.sqlprovider;
17
18 import static org.junit.jupiter.api.Assertions.assertEquals;
19
20 import java.io.Reader;
21 import java.lang.reflect.Method;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.stream.Collectors;
25
26 import org.apache.ibatis.BaseDataTest;
27 import org.apache.ibatis.annotations.DeleteProvider;
28 import org.apache.ibatis.annotations.InsertProvider;
29 import org.apache.ibatis.annotations.SelectProvider;
30 import org.apache.ibatis.annotations.UpdateProvider;
31 import org.apache.ibatis.builder.BuilderException;
32 import org.apache.ibatis.builder.annotation.ProviderContext;
33 import org.apache.ibatis.builder.annotation.ProviderMethodResolver;
34 import org.apache.ibatis.io.Resources;
35 import org.apache.ibatis.session.SqlSession;
36 import org.apache.ibatis.session.SqlSessionFactory;
37 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
38 import org.junit.jupiter.api.Assertions;
39 import org.junit.jupiter.api.BeforeAll;
40 import org.junit.jupiter.api.Test;
41
42
43
44
45 class ProviderMethodResolutionTest {
46
47 private static SqlSessionFactory sqlSessionFactory;
48
49 @BeforeAll
50 static void setUp() throws Exception {
51 try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/sqlprovider/mybatis-config.xml")) {
52 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
53 sqlSessionFactory.getConfiguration().addMapper(ProvideMethodResolverMapper.class);
54 }
55 BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
56 "org/apache/ibatis/submitted/sqlprovider/CreateDB.sql");
57 }
58
59 @Test
60 void shouldResolveWhenDefaultResolverMatchedMethodIsOne() {
61 try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
62 ProvideMethodResolverMapper mapper = sqlSession.getMapper(ProvideMethodResolverMapper.class);
63 assertEquals(1, mapper.select());
64 }
65 }
66
67 @Test
68 void shouldErrorWhenDefaultResolverMethodNameMatchedMethodIsNone() {
69 BuilderException e = Assertions.assertThrows(BuilderException.class, () -> sqlSessionFactory.getConfiguration()
70 .addMapper(DefaultProvideMethodResolverMethodNameMatchedMethodIsNoneMapper.class));
71 assertEquals(
72 "Cannot resolve the provider method because 'insert' not found in SqlProvider 'org.apache.ibatis.submitted.sqlprovider.ProviderMethodResolutionTest$DefaultProvideMethodResolverMethodNameMatchedMethodIsNoneMapper$MethodResolverBasedSqlProvider'.",
73 e.getMessage());
74 }
75
76 @Test
77 void shouldErrorWhenDefaultResolverReturnTypeMatchedMethodIsNone() {
78 BuilderException e = Assertions.assertThrows(BuilderException.class, () -> sqlSessionFactory.getConfiguration()
79 .addMapper(DefaultProvideMethodResolverReturnTypeMatchedMethodIsNoneMapper.class));
80 assertEquals(
81 "Cannot resolve the provider method because 'insert' does not return the CharSequence or its subclass in SqlProvider 'org.apache.ibatis.submitted.sqlprovider.ProviderMethodResolutionTest$DefaultProvideMethodResolverReturnTypeMatchedMethodIsNoneMapper$MethodResolverBasedSqlProvider'.",
82 e.getMessage());
83 }
84
85 @Test
86 void shouldErrorWhenDefaultResolverMatchedMethodIsMultiple() {
87 BuilderException e = Assertions.assertThrows(BuilderException.class, () -> sqlSessionFactory.getConfiguration()
88 .addMapper(DefaultProvideMethodResolverMatchedMethodIsMultipleMapper.class));
89 assertEquals(
90 "Cannot resolve the provider method because 'update' is found multiple in SqlProvider 'org.apache.ibatis.submitted.sqlprovider.ProviderMethodResolutionTest$DefaultProvideMethodResolverMatchedMethodIsMultipleMapper$MethodResolverBasedSqlProvider'.",
91 e.getMessage());
92 }
93
94 @Test
95 void shouldResolveReservedMethod() {
96 try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
97 ProvideMethodResolverMapper mapper = sqlSession.getMapper(ProvideMethodResolverMapper.class);
98 assertEquals(1, mapper.delete());
99 }
100 }
101
102 @Test
103 void shouldUseSpecifiedMethodOnSqlProviderAnnotation() {
104 try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
105 ProvideMethodResolverMapper mapper = sqlSession.getMapper(ProvideMethodResolverMapper.class);
106 assertEquals(2, mapper.select2());
107 }
108 }
109
110 @Test
111 void shouldResolveMethodUsingCustomResolver() {
112 try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
113 ProvideMethodResolverMapper mapper = sqlSession.getMapper(ProvideMethodResolverMapper.class);
114 assertEquals(3, mapper.select3());
115 }
116 }
117
118 @Test
119 void shouldResolveReservedNameMethodWhenCustomResolverReturnNull() {
120 try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
121 ProvideMethodResolverMapper mapper = sqlSession.getMapper(ProvideMethodResolverMapper.class);
122 assertEquals(99, mapper.select4());
123 }
124 }
125
126 @Test
127 void shouldErrorWhenCannotDetectsReservedNameMethod() {
128 BuilderException e = Assertions.assertThrows(BuilderException.class,
129 () -> sqlSessionFactory.getConfiguration().addMapper(ReservedNameMethodIsNoneMapper.class));
130 assertEquals(
131 "Error creating SqlSource for SqlProvider. Method 'provideSql' not found in SqlProvider 'org.apache.ibatis.submitted.sqlprovider.ProviderMethodResolutionTest$ReservedNameMethodIsNoneMapper$SqlProvider'.",
132 e.getMessage());
133 }
134
135 interface ProvideMethodResolverMapper {
136
137 @SelectProvider(MethodResolverBasedSqlProvider.class)
138 int select();
139
140 @SelectProvider(type = MethodResolverBasedSqlProvider.class, method = "provideSelect2Sql")
141 int select2();
142
143 @SelectProvider(type = CustomMethodResolverBasedSqlProvider.class)
144 int select3();
145
146 @SelectProvider(type = CustomMethodResolverBasedSqlProvider.class)
147 int select4();
148
149 @DeleteProvider(ReservedMethodNameBasedSqlProvider.class)
150 int delete();
151
152 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
153 public static String select() {
154 return "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
155 }
156
157 public static String select2() {
158 throw new IllegalStateException(
159 "This method should not called when specify `method` attribute on @SelectProvider.");
160 }
161
162 public static String provideSelect2Sql() {
163 return "SELECT 2 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
164 }
165 }
166
167 class ReservedMethodNameBasedSqlProvider {
168 public static String provideSql() {
169 return "DELETE FROM memos WHERE id = 1";
170 }
171
172 private ReservedMethodNameBasedSqlProvider() {
173 }
174 }
175
176 class CustomMethodResolverBasedSqlProvider implements CustomProviderMethodResolver {
177 public static String select3Sql() {
178 return "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
179 }
180
181 public static String provideSql() {
182 return "SELECT 99 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
183 }
184 }
185
186 }
187
188 interface CustomProviderMethodResolver extends ProviderMethodResolver {
189 @Override
190 default Method resolveMethod(ProviderContext context) {
191 List<Method> targetMethods = Arrays.stream(getClass().getMethods())
192 .filter(m -> m.getName().equals(context.getMapperMethod().getName() + "Sql"))
193 .filter(m -> CharSequence.class.isAssignableFrom(m.getReturnType())).collect(Collectors.toList());
194 if (targetMethods.size() == 1) {
195 return targetMethods.get(0);
196 }
197 return null;
198 }
199 }
200
201 interface DefaultProvideMethodResolverMethodNameMatchedMethodIsNoneMapper {
202
203 @InsertProvider(type = MethodResolverBasedSqlProvider.class)
204 int insert();
205
206 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
207 public static String provideInsertSql() {
208 return "INSERT INTO foo (name) VALUES(#{name})";
209 }
210 }
211
212 }
213
214 interface DefaultProvideMethodResolverReturnTypeMatchedMethodIsNoneMapper {
215
216 @InsertProvider(MethodResolverBasedSqlProvider.class)
217 int insert();
218
219 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
220 public static int insert() {
221 return 1;
222 }
223 }
224
225 }
226
227 interface DefaultProvideMethodResolverMatchedMethodIsMultipleMapper {
228
229 @UpdateProvider(MethodResolverBasedSqlProvider.class)
230 int update();
231
232 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
233 public static String update() {
234 return "UPDATE foo SET name = #{name} WHERE id = #{id}";
235 }
236
237 public static StringBuilder update(ProviderContext context) {
238 return new StringBuilder("UPDATE foo SET name = #{name} WHERE id = #{id}");
239 }
240 }
241
242 }
243
244 interface ReservedNameMethodIsNoneMapper {
245
246 @UpdateProvider(type = SqlProvider.class)
247 int update();
248
249 class SqlProvider {
250 public static String select() {
251 return "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
252 }
253
254 private SqlProvider() {
255 }
256 }
257
258 }
259
260 }