View Javadoc
1   /*
2    *    Copyright 2009-2024 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.scripting.defaults;
17  
18  import static org.mockito.ArgumentMatchers.any;
19  import static org.mockito.ArgumentMatchers.anyInt;
20  import static org.mockito.Mockito.doThrow;
21  import static org.mockito.Mockito.mock;
22  import static org.mockito.Mockito.verify;
23  import static org.mockito.Mockito.when;
24  
25  import java.sql.PreparedStatement;
26  import java.sql.SQLException;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.List;
30  
31  import org.apache.ibatis.builder.StaticSqlSource;
32  import org.apache.ibatis.domain.blog.Author;
33  import org.apache.ibatis.domain.blog.Section;
34  import org.apache.ibatis.mapping.BoundSql;
35  import org.apache.ibatis.mapping.MappedStatement;
36  import org.apache.ibatis.mapping.ParameterMapping;
37  import org.apache.ibatis.mapping.ResultMap;
38  import org.apache.ibatis.mapping.ResultMapping;
39  import org.apache.ibatis.mapping.SqlCommandType;
40  import org.apache.ibatis.reflection.DefaultReflectorFactory;
41  import org.apache.ibatis.reflection.MetaObject;
42  import org.apache.ibatis.reflection.ReflectorFactory;
43  import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
44  import org.apache.ibatis.reflection.factory.ObjectFactory;
45  import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
46  import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
47  import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
48  import org.apache.ibatis.session.Configuration;
49  import org.apache.ibatis.type.JdbcType;
50  import org.apache.ibatis.type.TypeException;
51  import org.apache.ibatis.type.TypeHandler;
52  import org.apache.ibatis.type.TypeHandlerRegistry;
53  import org.junit.jupiter.api.Assertions;
54  import org.junit.jupiter.api.Test;
55  
56  /**
57   * DefaultParameterHandlerTest
58   *
59   * @author Ryan Lamore
60   */
61  class DefaultParameterHandlerTest {
62  
63    @Test
64    void setParametersThrowsProperException() throws SQLException {
65      final MappedStatement mappedStatement = getMappedStatement();
66      final Object parameterObject = null;
67      final BoundSql boundSql = mock(BoundSql.class);
68  
69      TypeHandler<Object> typeHandler = mock(TypeHandler.class);
70      doThrow(new SQLException("foo")).when(typeHandler).setParameter(any(PreparedStatement.class), anyInt(), any(),
71          any(JdbcType.class));
72      ParameterMapping parameterMapping = new ParameterMapping.Builder(mappedStatement.getConfiguration(), "prop",
73          typeHandler).build();
74      List<ParameterMapping> parameterMappings = List.of(parameterMapping);
75      when(boundSql.getParameterMappings()).thenReturn(parameterMappings);
76  
77      DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
78          boundSql);
79  
80      PreparedStatement ps = mock(PreparedStatement.class);
81      try {
82        defaultParameterHandler.setParameters(ps);
83        Assertions.fail("Should have thrown TypeException");
84      } catch (Exception e) {
85        Assertions.assertTrue(e instanceof TypeException, "expected TypeException");
86        Assertions.assertTrue(e.getMessage().contains("mapping: ParameterMapping"));
87      }
88  
89    }
90  
91    MappedStatement getMappedStatement() {
92      final Configuration config = new Configuration();
93      final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
94      return new MappedStatement.Builder(config, "testSelect", new StaticSqlSource(config, "some select statement"),
95          SqlCommandType.SELECT).resultMaps(new ArrayList<ResultMap>() {
96            private static final long serialVersionUID = 1L;
97  
98            {
99              add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() {
100               private static final long serialVersionUID = 1L;
101 
102               {
103                 add(new ResultMapping.Builder(config, "cOlUmN1", "CoLuMn1", registry.getTypeHandler(Integer.class))
104                     .build());
105               }
106             }).build());
107           }
108         }).build();
109   }
110 
111   @Test
112   void parameterObjectGetPropertyValueWithAdditionalParameter() throws SQLException {
113     Configuration config = new Configuration();
114     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
115 
116     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
117         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
118 
119     Object parameterObject = 1;
120 
121     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
122       private static final long serialVersionUID = 1L;
123 
124       {
125         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
126       }
127     }, parameterObject) {
128       {
129         setAdditionalParameter("id", 2);
130       }
131     };
132 
133     DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
134         boundSql);
135 
136     PreparedStatement ps = mock(PreparedStatement.class);
137 
138     defaultParameterHandler.setParameters(ps);
139 
140     verify(ps).setInt(1, 2);
141   }
142 
143   @Test
144   void parameterObjectGetPropertyValueWithNull() throws SQLException {
145     Configuration config = new Configuration();
146     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
147 
148     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
149         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
150 
151     Object parameterObject = null;
152 
153     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
154       private static final long serialVersionUID = 1L;
155 
156       {
157         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
158       }
159     }, parameterObject);
160 
161     DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
162         boundSql);
163 
164     PreparedStatement ps = mock(PreparedStatement.class);
165 
166     defaultParameterHandler.setParameters(ps);
167 
168     verify(ps).setNull(1, config.getJdbcTypeForNull().TYPE_CODE);
169   }
170 
171   @Test
172   void parameterObjectGetPropertyValueWithTypeHandler() throws SQLException {
173     Configuration config = new Configuration();
174     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
175 
176     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
177         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
178 
179     Object parameterObject = 1;
180 
181     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
182       private static final long serialVersionUID = 1L;
183 
184       {
185         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
186       }
187     }, parameterObject);
188 
189     DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
190         boundSql);
191 
192     PreparedStatement ps = mock(PreparedStatement.class);
193 
194     defaultParameterHandler.setParameters(ps);
195 
196     verify(ps).setInt(1, (Integer) parameterObject);
197   }
198 
199   @Test
200   void parameterObjectGetPropertyValueWithMetaObject() throws SQLException {
201     Configuration config = new Configuration();
202     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
203 
204     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
205         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
206 
207     Author parameterObject = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
208 
209     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
210       private static final long serialVersionUID = 1L;
211 
212       {
213         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
214         add(new ParameterMapping.Builder(config, "username", registry.getTypeHandler(String.class)).build());
215         add(new ParameterMapping.Builder(config, "password", registry.getTypeHandler(String.class)).build());
216         add(new ParameterMapping.Builder(config, "email", registry.getTypeHandler(String.class)).build());
217         add(new ParameterMapping.Builder(config, "bio", registry.getTypeHandler(String.class))
218             .jdbcType(JdbcType.VARCHAR).build());
219         add(new ParameterMapping.Builder(config, "favouriteSection", registry.getTypeHandler(Section.class))
220             .jdbcType(JdbcType.VARCHAR).build());
221       }
222     }, parameterObject);
223 
224     DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
225         boundSql);
226 
227     PreparedStatement ps = mock(PreparedStatement.class);
228 
229     defaultParameterHandler.setParameters(ps);
230 
231     verify(ps).setInt(1, parameterObject.getId());
232     verify(ps).setString(2, parameterObject.getUsername());
233     verify(ps).setString(3, parameterObject.getPassword());
234     verify(ps).setString(4, parameterObject.getEmail());
235     verify(ps).setString(5, parameterObject.getBio());
236     verify(ps).setObject(6, parameterObject.getFavouriteSection().name(), JdbcType.VARCHAR.TYPE_CODE);
237   }
238 
239   @Test
240   void parameterObjectGetPropertyValueWithMetaObjectAndCreateOnce() {
241     Author parameterObject = mock(Author.class);
242 
243     Configuration mockConfig = mock(Configuration.class);
244 
245     final ObjectFactory objectFactory = new DefaultObjectFactory();
246     final ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
247     final ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
248 
249     when(mockConfig.getTypeHandlerRegistry()).thenReturn(new TypeHandlerRegistry(mockConfig));
250     when(mockConfig.getDefaultScriptingLanguageInstance()).thenReturn(new XMLLanguageDriver());
251     when(mockConfig.newMetaObject(parameterObject))
252         .thenReturn(MetaObject.forObject(parameterObject, objectFactory, objectWrapperFactory, reflectorFactory));
253 
254     TypeHandlerRegistry registry = mockConfig.getTypeHandlerRegistry();
255 
256     MappedStatement mappedStatement = new MappedStatement.Builder(mockConfig, "testSelect",
257         new StaticSqlSource(mockConfig, "some select statement"), SqlCommandType.SELECT).build();
258 
259     BoundSql boundSql = new BoundSql(mockConfig, "some select statement", new ArrayList<ParameterMapping>() {
260       private static final long serialVersionUID = 1L;
261 
262       {
263         add(new ParameterMapping.Builder(mockConfig, "id", registry.getTypeHandler(int.class))
264             .jdbcType(JdbcType.INTEGER).build());
265         add(new ParameterMapping.Builder(mockConfig, "username", registry.getTypeHandler(String.class))
266             .jdbcType(JdbcType.VARCHAR).build());
267         add(new ParameterMapping.Builder(mockConfig, "password", registry.getTypeHandler(String.class))
268             .jdbcType(JdbcType.VARCHAR).build());
269         add(new ParameterMapping.Builder(mockConfig, "email", registry.getTypeHandler(String.class))
270             .jdbcType(JdbcType.VARCHAR).build());
271         add(new ParameterMapping.Builder(mockConfig, "bio", registry.getTypeHandler(String.class))
272             .jdbcType(JdbcType.VARCHAR).build());
273         add(new ParameterMapping.Builder(mockConfig, "favouriteSection", registry.getTypeHandler(Section.class))
274             .jdbcType(JdbcType.VARCHAR).build());
275       }
276     }, parameterObject);
277 
278     DefaultParameterHandler defaultParameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject,
279         boundSql);
280 
281     PreparedStatement ps = mock(PreparedStatement.class);
282 
283     defaultParameterHandler.setParameters(ps);
284 
285     verify(parameterObject).getId();
286     verify(parameterObject).getUsername();
287     verify(parameterObject).getPassword();
288     verify(parameterObject).getEmail();
289     verify(parameterObject).getBio();
290     verify(parameterObject).getFavouriteSection();
291 
292     verify(mockConfig).newMetaObject(parameterObject);
293   }
294 }