1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.builder;
17
18 import static com.googlecode.catchexception.apis.BDDCatchException.caughtException;
19 import static com.googlecode.catchexception.apis.BDDCatchException.when;
20 import static org.assertj.core.api.Assertions.assertThat;
21 import static org.assertj.core.api.BDDAssertions.then;
22 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23 import static org.junit.jupiter.api.Assertions.assertEquals;
24 import static org.junit.jupiter.api.Assertions.assertNotNull;
25 import static org.junit.jupiter.api.Assertions.assertNull;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.StringReader;
31 import java.math.RoundingMode;
32 import java.sql.CallableStatement;
33 import java.sql.PreparedStatement;
34 import java.sql.ResultSet;
35 import java.sql.SQLException;
36 import java.util.Arrays;
37 import java.util.HashSet;
38 import java.util.Properties;
39
40 import org.apache.ibatis.builder.mapper.CustomMapper;
41 import org.apache.ibatis.builder.typehandler.CustomIntegerTypeHandler;
42 import org.apache.ibatis.builder.xml.XMLConfigBuilder;
43 import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
44 import org.apache.ibatis.domain.blog.Author;
45 import org.apache.ibatis.domain.blog.Blog;
46 import org.apache.ibatis.domain.blog.mappers.BlogMapper;
47 import org.apache.ibatis.domain.blog.mappers.NestedBlogMapper;
48 import org.apache.ibatis.domain.jpetstore.Cart;
49 import org.apache.ibatis.executor.loader.cglib.CglibProxyFactory;
50 import org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory;
51 import org.apache.ibatis.io.JBoss6VFS;
52 import org.apache.ibatis.io.Resources;
53 import org.apache.ibatis.logging.slf4j.Slf4jImpl;
54 import org.apache.ibatis.mapping.Environment;
55 import org.apache.ibatis.mapping.ResultSetType;
56 import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
57 import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
58 import org.apache.ibatis.session.AutoMappingBehavior;
59 import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
60 import org.apache.ibatis.session.Configuration;
61 import org.apache.ibatis.session.ExecutorType;
62 import org.apache.ibatis.session.LocalCacheScope;
63 import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
64 import org.apache.ibatis.type.BaseTypeHandler;
65 import org.apache.ibatis.type.EnumOrdinalTypeHandler;
66 import org.apache.ibatis.type.EnumTypeHandler;
67 import org.apache.ibatis.type.JdbcType;
68 import org.apache.ibatis.type.TypeHandler;
69 import org.apache.ibatis.type.TypeHandlerRegistry;
70 import org.junit.jupiter.api.Assertions;
71 import org.junit.jupiter.api.Tag;
72 import org.junit.jupiter.api.Test;
73
74 class XmlConfigBuilderTest {
75
76 @Test
77 void shouldSuccessfullyLoadMinimalXMLConfigFile() throws Exception {
78 String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml";
79 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
80 XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
81 Configuration config = builder.parse();
82 assertNotNull(config);
83 assertThat(config.getAutoMappingBehavior()).isEqualTo(AutoMappingBehavior.PARTIAL);
84 assertThat(config.getAutoMappingUnknownColumnBehavior()).isEqualTo(AutoMappingUnknownColumnBehavior.NONE);
85 assertThat(config.isCacheEnabled()).isTrue();
86 assertThat(config.getProxyFactory()).isInstanceOf(JavassistProxyFactory.class);
87 assertThat(config.isLazyLoadingEnabled()).isFalse();
88 assertThat(config.isAggressiveLazyLoading()).isFalse();
89 assertThat(config.isMultipleResultSetsEnabled()).isTrue();
90 assertThat(config.isUseColumnLabel()).isTrue();
91 assertThat(config.isUseGeneratedKeys()).isFalse();
92 assertThat(config.getDefaultExecutorType()).isEqualTo(ExecutorType.SIMPLE);
93 assertNull(config.getDefaultStatementTimeout());
94 assertNull(config.getDefaultFetchSize());
95 assertNull(config.getDefaultResultSetType());
96 assertThat(config.isMapUnderscoreToCamelCase()).isFalse();
97 assertThat(config.isSafeRowBoundsEnabled()).isFalse();
98 assertThat(config.getLocalCacheScope()).isEqualTo(LocalCacheScope.SESSION);
99 assertThat(config.getJdbcTypeForNull()).isEqualTo(JdbcType.OTHER);
100 assertThat(config.getLazyLoadTriggerMethods())
101 .isEqualTo(new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString")));
102 assertThat(config.isSafeResultHandlerEnabled()).isTrue();
103 assertThat(config.getDefaultScriptingLanuageInstance()).isInstanceOf(XMLLanguageDriver.class);
104 assertThat(config.isCallSettersOnNulls()).isFalse();
105 assertNull(config.getLogPrefix());
106 assertNull(config.getLogImpl());
107 assertNull(config.getConfigurationFactory());
108 assertThat(config.getTypeHandlerRegistry().getTypeHandler(RoundingMode.class))
109 .isInstanceOf(EnumTypeHandler.class);
110 assertThat(config.isShrinkWhitespacesInSql()).isFalse();
111 assertThat(config.isArgNameBasedConstructorAutoMapping()).isFalse();
112 assertThat(config.getDefaultSqlProviderType()).isNull();
113 assertThat(config.isNullableOnForEach()).isFalse();
114 }
115 }
116
117 enum MyEnum {
118
119 ONE,
120
121 TWO
122
123 }
124
125 public static class EnumOrderTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
126
127 private final E[] constants;
128
129 public EnumOrderTypeHandler(Class<E> javaType) {
130 constants = javaType.getEnumConstants();
131 }
132
133 @Override
134 public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
135 ps.setInt(i, parameter.ordinal() + 1);
136 }
137
138 @Override
139 public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
140 int index = rs.getInt(columnName) - 1;
141 return index < 0 ? null : constants[index];
142 }
143
144 @Override
145 public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
146 int index = rs.getInt(rs.getInt(columnIndex)) - 1;
147 return index < 0 ? null : constants[index];
148 }
149
150 @Override
151 public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
152 int index = cs.getInt(columnIndex) - 1;
153 return index < 0 ? null : constants[index];
154 }
155 }
156
157 @Test
158 void registerJavaTypeInitializingTypeHandler() {
159
160 final String MAPPER_CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
161 + "<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"https://mybatis.org/dtd/mybatis-3-config.dtd\">\n"
162 + "<configuration>\n"
163 + " <typeHandlers>\n"
164 + " <typeHandler javaType=\"org.apache.ibatis.builder.XmlConfigBuilderTest$MyEnum\"\n"
165 + " handler=\"org.apache.ibatis.builder.XmlConfigBuilderTest$EnumOrderTypeHandler\"/>\n"
166 + " </typeHandlers>\n"
167 + "</configuration>\n";
168
169
170 XMLConfigBuilder builder = new XMLConfigBuilder(new StringReader(MAPPER_CONFIG));
171 builder.parse();
172
173 TypeHandlerRegistry typeHandlerRegistry = builder.getConfiguration().getTypeHandlerRegistry();
174 TypeHandler<MyEnum> typeHandler = typeHandlerRegistry.getTypeHandler(MyEnum.class);
175
176 assertTrue(typeHandler instanceof EnumOrderTypeHandler);
177 assertArrayEquals(MyEnum.values(), ((EnumOrderTypeHandler<MyEnum>) typeHandler).constants);
178 }
179
180 @Tag("RequireIllegalAccess")
181 @Test
182 void shouldSuccessfullyLoadXMLConfigFile() throws Exception {
183 String resource = "org/apache/ibatis/builder/CustomizedSettingsMapperConfig.xml";
184 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
185 Properties props = new Properties();
186 props.put("prop2", "cccc");
187 XMLConfigBuilder builder = new XMLConfigBuilder(inputStream, null, props);
188 Configuration config = builder.parse();
189
190 assertThat(config.getAutoMappingBehavior()).isEqualTo(AutoMappingBehavior.NONE);
191 assertThat(config.getAutoMappingUnknownColumnBehavior()).isEqualTo(AutoMappingUnknownColumnBehavior.WARNING);
192 assertThat(config.isCacheEnabled()).isFalse();
193 assertThat(config.getProxyFactory()).isInstanceOf(CglibProxyFactory.class);
194 assertThat(config.isLazyLoadingEnabled()).isTrue();
195 assertThat(config.isAggressiveLazyLoading()).isTrue();
196 assertThat(config.isMultipleResultSetsEnabled()).isFalse();
197 assertThat(config.isUseColumnLabel()).isFalse();
198 assertThat(config.isUseGeneratedKeys()).isTrue();
199 assertThat(config.getDefaultExecutorType()).isEqualTo(ExecutorType.BATCH);
200 assertThat(config.getDefaultStatementTimeout()).isEqualTo(10);
201 assertThat(config.getDefaultFetchSize()).isEqualTo(100);
202 assertThat(config.getDefaultResultSetType()).isEqualTo(ResultSetType.SCROLL_INSENSITIVE);
203 assertThat(config.isMapUnderscoreToCamelCase()).isTrue();
204 assertThat(config.isSafeRowBoundsEnabled()).isTrue();
205 assertThat(config.getLocalCacheScope()).isEqualTo(LocalCacheScope.STATEMENT);
206 assertThat(config.getJdbcTypeForNull()).isEqualTo(JdbcType.NULL);
207 assertThat(config.getLazyLoadTriggerMethods())
208 .isEqualTo(new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString", "xxx")));
209 assertThat(config.isSafeResultHandlerEnabled()).isFalse();
210 assertThat(config.getDefaultScriptingLanuageInstance()).isInstanceOf(RawLanguageDriver.class);
211 assertThat(config.isCallSettersOnNulls()).isTrue();
212 assertThat(config.getLogPrefix()).isEqualTo("mybatis_");
213 assertThat(config.getLogImpl().getName()).isEqualTo(Slf4jImpl.class.getName());
214 assertThat(config.getVfsImpl().getName()).isEqualTo(JBoss6VFS.class.getName());
215 assertThat(config.getConfigurationFactory().getName()).isEqualTo(String.class.getName());
216 assertThat(config.isShrinkWhitespacesInSql()).isTrue();
217 assertThat(config.isArgNameBasedConstructorAutoMapping()).isTrue();
218 assertThat(config.getDefaultSqlProviderType().getName()).isEqualTo(MySqlProvider.class.getName());
219 assertThat(config.isNullableOnForEach()).isTrue();
220
221 assertThat(config.getTypeAliasRegistry().getTypeAliases().get("blogauthor")).isEqualTo(Author.class);
222 assertThat(config.getTypeAliasRegistry().getTypeAliases().get("blog")).isEqualTo(Blog.class);
223 assertThat(config.getTypeAliasRegistry().getTypeAliases().get("cart")).isEqualTo(Cart.class);
224
225 assertThat(config.getTypeHandlerRegistry().getTypeHandler(Integer.class))
226 .isInstanceOf(CustomIntegerTypeHandler.class);
227 assertThat(config.getTypeHandlerRegistry().getTypeHandler(Long.class)).isInstanceOf(CustomLongTypeHandler.class);
228 assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class))
229 .isInstanceOf(CustomStringTypeHandler.class);
230 assertThat(config.getTypeHandlerRegistry().getTypeHandler(String.class, JdbcType.VARCHAR))
231 .isInstanceOf(CustomStringTypeHandler.class);
232 assertThat(config.getTypeHandlerRegistry().getTypeHandler(RoundingMode.class))
233 .isInstanceOf(EnumOrdinalTypeHandler.class);
234
235 ExampleObjectFactory objectFactory = (ExampleObjectFactory) config.getObjectFactory();
236 assertThat(objectFactory.getProperties().size()).isEqualTo(1);
237 assertThat(objectFactory.getProperties().getProperty("objectFactoryProperty")).isEqualTo("100");
238
239 assertThat(config.getObjectWrapperFactory()).isInstanceOf(CustomObjectWrapperFactory.class);
240
241 assertThat(config.getReflectorFactory()).isInstanceOf(CustomReflectorFactory.class);
242
243 ExamplePlugin plugin = (ExamplePlugin) config.getInterceptors().get(0);
244 assertThat(plugin.getProperties().size()).isEqualTo(1);
245 assertThat(plugin.getProperties().getProperty("pluginProperty")).isEqualTo("100");
246
247 Environment environment = config.getEnvironment();
248 assertThat(environment.getId()).isEqualTo("development");
249 assertThat(environment.getDataSource()).isInstanceOf(UnpooledDataSource.class);
250 assertThat(environment.getTransactionFactory()).isInstanceOf(JdbcTransactionFactory.class);
251
252 assertThat(config.getDatabaseId()).isEqualTo("derby");
253
254 assertThat(config.getMapperRegistry().getMappers().size()).isEqualTo(4);
255 assertThat(config.getMapperRegistry().hasMapper(CachedAuthorMapper.class)).isTrue();
256 assertThat(config.getMapperRegistry().hasMapper(CustomMapper.class)).isTrue();
257 assertThat(config.getMapperRegistry().hasMapper(BlogMapper.class)).isTrue();
258 assertThat(config.getMapperRegistry().hasMapper(NestedBlogMapper.class)).isTrue();
259 }
260 }
261
262 @Test
263 void shouldSuccessfullyLoadXMLConfigFileWithPropertiesUrl() throws Exception {
264 String resource = "org/apache/ibatis/builder/PropertiesUrlMapperConfig.xml";
265 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
266 XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
267 Configuration config = builder.parse();
268 assertThat(config.getVariables().get("driver").toString()).isEqualTo("org.apache.derby.jdbc.EmbeddedDriver");
269 assertThat(config.getVariables().get("prop1").toString()).isEqualTo("bbbb");
270 }
271 }
272
273 @Test
274 void parseIsTwice() throws Exception {
275 String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml";
276 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
277 XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
278 builder.parse();
279
280 when(builder::parse);
281 then(caughtException()).isInstanceOf(BuilderException.class)
282 .hasMessage("Each XMLConfigBuilder can only be used once.");
283 }
284 }
285
286 @Test
287 void unknownSettings() {
288
289 final String MAPPER_CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
290 + "<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"https://mybatis.org/dtd/mybatis-3-config.dtd\">\n"
291 + "<configuration>\n"
292 + " <settings>\n"
293 + " <setting name=\"foo\" value=\"bar\"/>\n"
294 + " </settings>\n"
295 + "</configuration>\n";
296
297
298 XMLConfigBuilder builder = new XMLConfigBuilder(new StringReader(MAPPER_CONFIG));
299 when(builder::parse);
300 then(caughtException()).isInstanceOf(BuilderException.class)
301 .hasMessageContaining("The setting foo is not known. Make sure you spelled it correctly (case sensitive).");
302 }
303
304 @Test
305 void unknownJavaTypeOnTypeHandler() {
306
307 final String MAPPER_CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
308 + "<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"https://mybatis.org/dtd/mybatis-3-config.dtd\">\n"
309 + "<configuration>\n"
310 + " <typeAliases>\n"
311 + " <typeAlias type=\"a.b.c.Foo\"/>\n"
312 + " </typeAliases>\n"
313 + "</configuration>\n";
314
315
316 XMLConfigBuilder builder = new XMLConfigBuilder(new StringReader(MAPPER_CONFIG));
317 when(builder::parse);
318 then(caughtException()).isInstanceOf(BuilderException.class)
319 .hasMessageContaining("Error registering typeAlias for 'null'. Cause: ");
320 }
321
322 @Test
323 void propertiesSpecifyResourceAndUrlAtSameTime() {
324
325 final String MAPPER_CONFIG = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
326 + "<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"https://mybatis.org/dtd/mybatis-3-config.dtd\">\n"
327 + "<configuration>\n"
328 + " <properties resource=\"a/b/c/foo.properties\" url=\"file:./a/b/c/jdbc.properties\"/>\n"
329 + "</configuration>\n";
330
331
332 XMLConfigBuilder builder = new XMLConfigBuilder(new StringReader(MAPPER_CONFIG));
333 when(builder::parse);
334 then(caughtException()).isInstanceOf(BuilderException.class).hasMessageContaining(
335 "The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
336 }
337
338 static class MySqlProvider {
339 @SuppressWarnings("unused")
340 public static String provideSql() {
341 return "SELECT 1";
342 }
343
344 private MySqlProvider() {
345 }
346 }
347
348 @Test
349 void shouldAllowSubclassedConfiguration() throws IOException {
350 String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml";
351 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
352 XMLConfigBuilder builder = new XMLConfigBuilder(MyConfiguration.class, inputStream, null, null);
353 Configuration config = builder.parse();
354
355 assertThat(config).isInstanceOf(MyConfiguration.class);
356 }
357 }
358
359 @Test
360 void noDefaultConstructorForSubclassedConfiguration() throws IOException {
361 String resource = "org/apache/ibatis/builder/MinimalMapperConfig.xml";
362 try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
363 Exception exception = Assertions.assertThrows(Exception.class,
364 () -> new XMLConfigBuilder(BadConfiguration.class, inputStream, null, null));
365 assertEquals("Failed to create a new Configuration instance.", exception.getMessage());
366 }
367 }
368
369 public static class MyConfiguration extends Configuration {
370
371 }
372
373 public static class BadConfiguration extends Configuration {
374
375 public BadConfiguration(String parameter) {
376
377 }
378 }
379
380 }