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