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
173 class CustomMethodResolverBasedSqlProvider implements CustomProviderMethodResolver {
174 public static String select3Sql() {
175 return "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
176 }
177
178 public static String provideSql() {
179 return "SELECT 99 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
180 }
181 }
182
183 }
184
185 interface CustomProviderMethodResolver extends ProviderMethodResolver {
186 @Override
187 default Method resolveMethod(ProviderContext context) {
188 List<Method> targetMethods = Arrays.stream(getClass().getMethods())
189 .filter(m -> m.getName().equals(context.getMapperMethod().getName() + "Sql"))
190 .filter(m -> CharSequence.class.isAssignableFrom(m.getReturnType())).collect(Collectors.toList());
191 if (targetMethods.size() == 1) {
192 return targetMethods.get(0);
193 }
194 return null;
195 }
196 }
197
198 interface DefaultProvideMethodResolverMethodNameMatchedMethodIsNoneMapper {
199
200 @InsertProvider(type = MethodResolverBasedSqlProvider.class)
201 int insert();
202
203 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
204 public static String provideInsertSql() {
205 return "INSERT INTO foo (name) VALUES(#{name})";
206 }
207 }
208
209 }
210
211 interface DefaultProvideMethodResolverReturnTypeMatchedMethodIsNoneMapper {
212
213 @InsertProvider(MethodResolverBasedSqlProvider.class)
214 int insert();
215
216 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
217 public static int insert() {
218 return 1;
219 }
220 }
221
222 }
223
224 interface DefaultProvideMethodResolverMatchedMethodIsMultipleMapper {
225
226 @UpdateProvider(MethodResolverBasedSqlProvider.class)
227 int update();
228
229 class MethodResolverBasedSqlProvider implements ProviderMethodResolver {
230 public static String update() {
231 return "UPDATE foo SET name = #{name} WHERE id = #{id}";
232 }
233
234 public static StringBuilder update(ProviderContext context) {
235 return new StringBuilder("UPDATE foo SET name = #{name} WHERE id = #{id}");
236 }
237 }
238
239 }
240
241 interface ReservedNameMethodIsNoneMapper {
242
243 @UpdateProvider(type = SqlProvider.class)
244 int update();
245
246 class SqlProvider {
247 public static String select() {
248 return "SELECT 1 FROM INFORMATION_SCHEMA.SYSTEM_USERS";
249 }
250 }
251
252 }
253
254 }