View Javadoc
1   /*
2    *    Copyright 2009-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.submitted.member_access;
17  
18  import static org.junit.jupiter.api.Assertions.assertEquals;
19  
20  import java.io.Reader;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import org.apache.ibatis.annotations.Arg;
25  import org.apache.ibatis.annotations.ConstructorArgs;
26  import org.apache.ibatis.annotations.Result;
27  import org.apache.ibatis.annotations.Results;
28  import org.apache.ibatis.annotations.Select;
29  import org.apache.ibatis.io.Resources;
30  import org.apache.ibatis.session.SqlSession;
31  import org.apache.ibatis.session.SqlSessionFactory;
32  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
33  import org.junit.jupiter.api.BeforeAll;
34  import org.junit.jupiter.api.Test;
35  
36  /**
37   * Tests for member access of Java Object.
38   */
39  class MemberAccessTest {
40  
41    private static SqlSessionFactory sqlSessionFactory;
42  
43    @BeforeAll
44    static void setUp() throws Exception {
45      try (
46          Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/member_access/mybatis-config.xml")) {
47        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
48        sqlSessionFactory.getConfiguration().addMapper(Mapper.class);
49      }
50    }
51  
52    @Test
53    void parameterMappingAndResultAutoMapping() {
54      try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
55        Mapper mapper = sqlSession.getMapper(Mapper.class);
56  
57        Params params = new Params();
58        Bean bean = mapper.resultAutoMapping(params);
59  
60        assertEquals(params.privateField, bean.privateField);
61        assertEquals(params.packagePrivateField, bean.packagePrivateField);
62        assertEquals(params.protectedField, bean.protectedField);
63        assertEquals(params.publicField, bean.publicField);
64        assertEquals(params.getPrivateProperty(), bean.properties.get("privateProperty"));
65        assertEquals(params.getPackagePrivateProperty(), bean.properties.get("packagePrivateProperty"));
66        assertEquals(params.getProtectedProperty(), bean.properties.get("protectedProperty"));
67        assertEquals(params.getPublicProperty(), bean.properties.get("publicProperty"));
68      }
69    }
70  
71    @Test // gh-1258
72    void parameterMappingAndResultAutoMappingUsingOgnl() {
73      try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
74        Mapper mapper = sqlSession.getMapper(Mapper.class);
75  
76        Params params = new Params();
77        Bean bean = mapper.resultAutoMappingUsingOgnl(params);
78  
79        assertEquals(params.privateField + "%", bean.privateField);
80        assertEquals(params.packagePrivateField + "%", bean.packagePrivateField);
81        assertEquals(params.protectedField + "%", bean.protectedField);
82        assertEquals(params.publicField + "%", bean.publicField);
83        assertEquals(params.getPrivateProperty() + "%", bean.properties.get("privateProperty"));
84        assertEquals(params.getPackagePrivateProperty() + "%", bean.properties.get("packagePrivateProperty"));
85        assertEquals(params.getProtectedProperty() + "%", bean.properties.get("protectedProperty"));
86        assertEquals(params.getPublicProperty() + "%", bean.properties.get("publicProperty"));
87      }
88    }
89  
90    @Test
91    void parameterMappingAndResultMapping() {
92      try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
93        Mapper mapper = sqlSession.getMapper(Mapper.class);
94  
95        Params params = new Params();
96        Bean bean = mapper.resultMapping(params);
97  
98        assertEquals(params.privateField, bean.privateField);
99        assertEquals(params.packagePrivateField, bean.packagePrivateField);
100       assertEquals(params.protectedField, bean.protectedField);
101       assertEquals(params.publicField, bean.publicField);
102       assertEquals(params.getPrivateProperty(), bean.properties.get("privateProperty"));
103       assertEquals(params.getPackagePrivateProperty(), bean.properties.get("packagePrivateProperty"));
104       assertEquals(params.getProtectedProperty(), bean.properties.get("protectedProperty"));
105       assertEquals(params.getPublicProperty(), bean.properties.get("publicProperty"));
106     }
107   }
108 
109   @Test
110   void constructorAutoMapping() {
111     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
112       Mapper mapper = sqlSession.getMapper(Mapper.class);
113 
114       {
115         Immutable immutable = mapper.privateConstructorAutoMapping();
116         assertEquals(1, immutable.properties.size());
117         assertEquals("1", immutable.properties.get("arg1"));
118       }
119 
120       {
121         Immutable immutable = mapper.packagePrivateConstructorAutoMapping();
122         assertEquals(2, immutable.properties.size());
123         assertEquals("1", immutable.properties.get("arg1"));
124         assertEquals("2", immutable.properties.get("arg2"));
125       }
126 
127       {
128         Immutable immutable = mapper.protectedConstructorAutoMapping();
129         assertEquals(3, immutable.properties.size());
130         assertEquals("1", immutable.properties.get("arg1"));
131         assertEquals("2", immutable.properties.get("arg2"));
132         assertEquals("3", immutable.properties.get("arg3"));
133       }
134 
135       {
136         Immutable immutable = mapper.publicConstructorAutoMapping();
137         assertEquals(4, immutable.properties.size());
138         assertEquals("1", immutable.properties.get("arg1"));
139         assertEquals("2", immutable.properties.get("arg2"));
140         assertEquals("3", immutable.properties.get("arg3"));
141         assertEquals("4", immutable.properties.get("arg4"));
142       }
143     }
144 
145   }
146 
147   @Test
148   void constructorMapping() {
149     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
150       Mapper mapper = sqlSession.getMapper(Mapper.class);
151 
152       {
153         Immutable immutable = mapper.privateConstructorMapping();
154         assertEquals(1, immutable.properties.size());
155         assertEquals("1", immutable.properties.get("arg1"));
156       }
157 
158       {
159         Immutable immutable = mapper.packagePrivateConstructorMapping();
160         assertEquals(2, immutable.properties.size());
161         assertEquals("1", immutable.properties.get("arg1"));
162         assertEquals("2", immutable.properties.get("arg2"));
163       }
164 
165       {
166         Immutable immutable = mapper.protectedConstructorMapping();
167         assertEquals(3, immutable.properties.size());
168         assertEquals("1", immutable.properties.get("arg1"));
169         assertEquals("2", immutable.properties.get("arg2"));
170         assertEquals("3", immutable.properties.get("arg3"));
171       }
172 
173       {
174         Immutable immutable = mapper.publicConstructorMapping();
175         assertEquals(4, immutable.properties.size());
176         assertEquals("1", immutable.properties.get("arg1"));
177         assertEquals("2", immutable.properties.get("arg2"));
178         assertEquals("3", immutable.properties.get("arg3"));
179         assertEquals("4", immutable.properties.get("arg4"));
180       }
181     }
182 
183   }
184 
185   interface Mapper {
186     // @formatter:off
187     @Select({
188           "SELECT"
189         , "#{privateField} as privateField"
190         , ",#{packagePrivateField} as packagePrivateField"
191         , ",#{protectedField} as protectedField"
192         , ",#{publicField} as publicField"
193         , ",#{privateProperty} as privateProperty"
194         , ",#{packagePrivateProperty} as packagePrivateProperty"
195         , ",#{protectedProperty} as protectedProperty"
196         , ",#{publicProperty} as publicProperty"
197         , "FROM"
198         , "INFORMATION_SCHEMA.SYSTEM_USERS"
199     })
200     // @formatter:on
201     Bean resultAutoMapping(Params params);
202 
203     // @formatter:off
204     @Select({
205         "<script>"
206 
207           , "<bind name=\"privateFieldValue\" value=\"_parameter.privateField + '%'\" />"
208           , "<bind name=\"packagePrivateFieldValue\" value=\"_parameter.packagePrivateField + '%'\" />"
209           , "<bind name=\"protectedFieldValue\" value=\"_parameter.protectedField + '%'\" />"
210           , "<bind name=\"publicFieldValue\" value=\"_parameter.publicField + '%'\" />"
211           , "<bind name=\"privatePropertyValue\" value=\"_parameter.privateProperty + '%'\" />"
212           , "<bind name=\"packagePrivatePropertyValue\" value=\"_parameter.packagePrivateProperty + '%'\" />"
213           , "<bind name=\"protectedPropertyValue\" value=\"_parameter.getProtectedProperty() + '%'\" />"
214           , "<bind name=\"publicPropertyValue\" value=\"_parameter.publicProperty + '%'\" />"
215 
216           , "SELECT"
217           , "#{privateFieldValue} as privateField"
218           , ",#{packagePrivateFieldValue} as packagePrivateField"
219           , ",#{protectedFieldValue} as protectedField"
220           , ",#{publicFieldValue} as publicField"
221           , ",#{privatePropertyValue} as privateProperty"
222           , ",#{packagePrivatePropertyValue} as packagePrivateProperty"
223           , ",#{protectedPropertyValue} as protectedProperty"
224           , ",#{publicPropertyValue} as publicProperty"
225 
226           , "FROM INFORMATION_SCHEMA.SYSTEM_USERS"
227 
228         , "</script>"}
229     )
230     // @formatter:on
231     Bean resultAutoMappingUsingOgnl(Params params);
232 
233     // @formatter:off
234     @Results({
235         @Result(property = "privateField", column = "private_field")
236         , @Result(property = "packagePrivateField", column = "package_private_field")
237         , @Result(property = "protectedField", column = "protected_field")
238         , @Result(property = "publicField", column = "public_field")
239         , @Result(property = "privateProperty", column = "private_property")
240         , @Result(property = "packagePrivateProperty", column = "package_private_property")
241         , @Result(property = "protectedProperty", column = "protected_property")
242         , @Result(property = "publicProperty", column = "public_property")
243     })
244     @Select({
245           "SELECT"
246         , "#{privateField} as private_field"
247         , ",#{packagePrivateField} as package_private_field"
248         , ",#{protectedField} as protected_field"
249         , ",#{publicField} as public_field"
250         , ",#{privateProperty} as private_property"
251         , ",#{packagePrivateProperty} as package_private_property"
252         , ",#{protectedProperty} as protected_property"
253         , ",#{publicProperty} as public_property"
254         , "FROM INFORMATION_SCHEMA.SYSTEM_USERS"
255     })
256     // @formatter:on
257     Bean resultMapping(Params params);
258 
259     @Select("SELECT '1' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
260     Immutable privateConstructorAutoMapping();
261 
262     @Select("SELECT '1', '2' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
263     Immutable packagePrivateConstructorAutoMapping();
264 
265     @Select("SELECT '1', '2', '3' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
266     Immutable protectedConstructorAutoMapping();
267 
268     @Select("SELECT '1', '2', '3', '4' FROM INFORMATION_SCHEMA.SYSTEM_USERS")
269     Immutable publicConstructorAutoMapping();
270 
271     @ConstructorArgs({ @Arg(column = "c1", javaType = String.class) })
272     @Select("SELECT '1' as c1 FROM INFORMATION_SCHEMA.SYSTEM_USERS")
273     Immutable privateConstructorMapping();
274 
275     // @formatter:off
276     @ConstructorArgs({
277           @Arg(column = "c1", javaType = String.class)
278         , @Arg(column = "c2", javaType = String.class)
279     })
280     // @formatter:on
281     @Select("SELECT '1' as c1, '2' as c2 FROM INFORMATION_SCHEMA.SYSTEM_USERS")
282     Immutable packagePrivateConstructorMapping();
283 
284     // @formatter:off
285     @ConstructorArgs({
286           @Arg(column = "c1", javaType = String.class)
287         , @Arg(column = "c2", javaType = String.class)
288         , @Arg(column = "c3", javaType = String.class)
289     })
290     // @formatter:on
291     @Select("SELECT '1' as c1, '2' as c2, '3' as c3 FROM INFORMATION_SCHEMA.SYSTEM_USERS")
292     Immutable protectedConstructorMapping();
293 
294     // @formatter:off
295     @ConstructorArgs({
296           @Arg(column = "c1", javaType = String.class)
297         , @Arg(column = "c2", javaType = String.class)
298         , @Arg(column = "c3", javaType = String.class)
299         , @Arg(column = "c4", javaType = String.class)
300     })
301     // @formatter:on
302     @Select("SELECT '1' as c1, '2' as c2, '3' as c3, '4' as c4 FROM INFORMATION_SCHEMA.SYSTEM_USERS")
303     Immutable publicConstructorMapping();
304 
305   }
306 
307   static class Params {
308     private final String privateField = "privateField";
309     String packagePrivateField = "packagePrivateField";
310     protected String protectedField = "protectedField";
311     public String publicField = "publicField";
312 
313     private String getPrivateProperty() {
314       return "privateProperty";
315     }
316 
317     String getPackagePrivateProperty() {
318       return "packagePrivateProperty";
319     }
320 
321     protected String getProtectedProperty() {
322       return "protectedProperty";
323     }
324 
325     public String getPublicProperty() {
326       return "publicProperty";
327     }
328   }
329 
330   @SuppressWarnings("unused")
331   static class Bean {
332     private String privateField;
333     String packagePrivateField;
334     protected String protectedField;
335     public String publicField;
336     private Map<String, String> properties = new HashMap<>();
337 
338     private void setPrivateProperty(String value) {
339       properties.put("privateProperty", value);
340     }
341 
342     void setPackagePrivateProperty(String value) {
343       properties.put("packagePrivateProperty", value);
344     }
345 
346     protected void setProtectedProperty(String value) {
347       properties.put("protectedProperty", value);
348     }
349 
350     public void setPublicProperty(String value) {
351       properties.put("publicProperty", value);
352     }
353   }
354 
355   @SuppressWarnings("unused")
356   static class Immutable {
357     private Map<String, String> properties = new HashMap<>();
358 
359     private Immutable(String arg1) {
360       properties.put("arg1", arg1);
361     }
362 
363     Immutable(String arg1, String arg2) {
364       properties.put("arg1", arg1);
365       properties.put("arg2", arg2);
366     }
367 
368     protected Immutable(String arg1, String arg2, String arg3) {
369       properties.put("arg1", arg1);
370       properties.put("arg2", arg2);
371       properties.put("arg3", arg3);
372     }
373 
374     public Immutable(String arg1, String arg2, String arg3, String arg4) {
375       properties.put("arg1", arg1);
376       properties.put("arg2", arg2);
377       properties.put("arg3", arg3);
378       properties.put("arg4", arg4);
379     }
380 
381   }
382 
383 }