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