View Javadoc
1   /*
2    *    Copyright 2015-2025 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.mybatis.spring.boot.autoconfigure;
17  
18  import static org.assertj.core.api.Assertions.assertThat;
19  import static org.junit.jupiter.api.Assertions.assertAll;
20  
21  import java.beans.PropertyDescriptor;
22  import java.util.Arrays;
23  import java.util.HashSet;
24  import java.util.Set;
25  import java.util.stream.Collectors;
26  
27  import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
28  import org.apache.ibatis.mapping.ResultSetType;
29  import org.apache.ibatis.session.AutoMappingBehavior;
30  import org.apache.ibatis.session.AutoMappingUnknownColumnBehavior;
31  import org.apache.ibatis.session.Configuration;
32  import org.apache.ibatis.session.ExecutorType;
33  import org.apache.ibatis.session.LocalCacheScope;
34  import org.apache.ibatis.session.SqlSessionFactory;
35  import org.apache.ibatis.type.EnumOrdinalTypeHandler;
36  import org.apache.ibatis.type.EnumTypeHandler;
37  import org.apache.ibatis.type.JdbcType;
38  import org.junit.jupiter.api.Test;
39  import org.springframework.beans.PropertyAccessorFactory;
40  import org.springframework.boot.autoconfigure.AutoConfigurations;
41  import org.springframework.boot.jdbc.autoconfigure.EmbeddedDataSourceConfiguration;
42  import org.springframework.boot.test.context.runner.ApplicationContextRunner;
43  
44  /**
45   * @author Eddú Meléndez
46   */
47  class MybatisPropertiesTest {
48  
49    private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
50        .withConfiguration(AutoConfigurations.of(MybatisAutoConfiguration.class));
51  
52    @Test
53    void emptyMapperLocations() {
54      MybatisProperties properties = new MybatisProperties();
55      assertThat(properties.resolveMapperLocations()).isEmpty();
56    }
57  
58    @Test
59    void twoLocations() {
60      MybatisProperties properties = new MybatisProperties();
61      properties
62          .setMapperLocations(new String[] { "classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml",
63              "classpath:org/mybatis/spring/boot/autoconfigure/repository/*Mapper.xml" });
64      assertThat(properties.resolveMapperLocations()).hasSize(2);
65    }
66  
67    @Test
68    void twoLocationsWithOneIncorrectLocation() {
69      MybatisProperties properties = new MybatisProperties();
70      properties
71          .setMapperLocations(new String[] { "classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml",
72              "classpath:org/mybatis/spring/boot/autoconfigure/repositoy/*Mapper.xml" });
73      assertThat(properties.resolveMapperLocations()).hasSize(1);
74    }
75  
76    @Test
77    void testWithDefaultCoreConfiguration() {
78      this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run(context -> {
79        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isSafeRowBoundsEnabled()).isFalse();
80        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isSafeResultHandlerEnabled()).isTrue();
81        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isSafeResultHandlerEnabled()).isTrue();
82        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isAggressiveLazyLoading()).isFalse();
83        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMultipleResultSetsEnabled()).isTrue();
84        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isUseGeneratedKeys()).isFalse();
85        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isUseColumnLabel()).isTrue();
86        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isCacheEnabled()).isTrue();
87        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isCallSettersOnNulls()).isFalse();
88        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isUseActualParamName()).isTrue();
89        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isReturnInstanceForEmptyRow()).isFalse();
90        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isShrinkWhitespacesInSql()).isFalse();
91        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isNullableOnForEach()).isFalse();
92        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isArgNameBasedConstructorAutoMapping())
93            .isFalse();
94        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isLazyLoadingEnabled()).isFalse();
95        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultStatementTimeout()).isNull();
96        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultFetchSize()).isNull();
97        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLocalCacheScope())
98            .isEqualTo(LocalCacheScope.SESSION);
99        assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getJdbcTypeForNull())
100           .isEqualTo(JdbcType.OTHER);
101       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultResultSetType()).isNull();
102       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultExecutorType())
103           .isEqualTo(ExecutorType.SIMPLE);
104       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getAutoMappingBehavior())
105           .isEqualTo(AutoMappingBehavior.PARTIAL);
106       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getAutoMappingUnknownColumnBehavior())
107           .isEqualTo(AutoMappingUnknownColumnBehavior.NONE);
108       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLogPrefix()).isNull();
109       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLazyLoadTriggerMethods())
110           .isEqualTo(new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString")));
111       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLogImpl()).isNull();
112       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVfsImpl())
113           .isEqualTo(SpringBootVFS.class);
114       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultSqlProviderType()).isNull();
115       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getTypeHandlerRegistry()
116           .getTypeHandler(JdbcType.class).getClass()).isEqualTo(EnumTypeHandler.class);
117       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getConfigurationFactory()).isNull();
118       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVariables()).hasToString("{}");
119       assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDatabaseId()).isNull();
120     });
121   }
122 
123   @Test
124   void testWithCustomizeCoreConfiguration() {
125     assertAll(
126         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
127             .withPropertyValues("mybatis.configuration.safe-row-bounds-enabled:true")
128             .run(context -> assertThat(
129                 context.getBean(SqlSessionFactory.class).getConfiguration().isSafeRowBoundsEnabled()).isTrue()),
130         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
131             .withPropertyValues("mybatis.configuration.safe-result-handler-enabled:false")
132             .run(context -> assertThat(
133                 context.getBean(SqlSessionFactory.class).getConfiguration().isSafeResultHandlerEnabled()).isFalse()),
134         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
135             .withPropertyValues("mybatis.configuration.safe-result-handler-enabled:false")
136             .run(context -> assertThat(
137                 context.getBean(SqlSessionFactory.class).getConfiguration().isSafeResultHandlerEnabled()).isFalse()),
138         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
139             .withPropertyValues("mybatis.configuration.aggressive-lazy-loading:true")
140             .run(context -> assertThat(
141                 context.getBean(SqlSessionFactory.class).getConfiguration().isAggressiveLazyLoading()).isTrue()),
142         // Since MyBatis 3.5.17, the return value of isMultipleResultSetsEnabled() is always true
143         // See https://github.com/mybatis/mybatis-3/pull/3238
144         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
145             .withPropertyValues("mybatis.configuration.multiple-result-sets-enabled:false")
146             .run(context -> assertThat(
147                 context.getBean(SqlSessionFactory.class).getConfiguration().isMultipleResultSetsEnabled()).isTrue()),
148         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
149             .withPropertyValues("mybatis.configuration.use-generated-keys:true")
150             .run(context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isUseGeneratedKeys())
151                 .isTrue()),
152         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
153             .withPropertyValues("mybatis.configuration.use-column-label:false")
154             .run(context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isUseColumnLabel())
155                 .isFalse()),
156         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
157             .withPropertyValues("mybatis.configuration.cache-enabled:false")
158             .run(context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isCacheEnabled())
159                 .isFalse()),
160         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
161             .withPropertyValues("mybatis.configuration.call-setters-on-nulls:true")
162             .run(context -> assertThat(
163                 context.getBean(SqlSessionFactory.class).getConfiguration().isCallSettersOnNulls()).isTrue()),
164         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
165             .withPropertyValues("mybatis.configuration.use-actual-paramName:false")
166             .run(context -> assertThat(
167                 context.getBean(SqlSessionFactory.class).getConfiguration().isUseActualParamName()).isFalse()),
168         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
169             .withPropertyValues("mybatis.configuration.return-instance-for-empty-row:true")
170             .run(context -> assertThat(
171                 context.getBean(SqlSessionFactory.class).getConfiguration().isReturnInstanceForEmptyRow()).isTrue()),
172         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
173             .withPropertyValues("mybatis.configuration.shrink-whitespaces-in-sql:true")
174             .run(context -> assertThat(
175                 context.getBean(SqlSessionFactory.class).getConfiguration().isShrinkWhitespacesInSql()).isTrue()),
176         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
177             .withPropertyValues("mybatis.configuration.nullable-on-for-each:true").run(
178                 context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isNullableOnForEach())
179                     .isTrue()),
180         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
181             .withPropertyValues("mybatis.configuration.arg-name-based-constructor-auto-mapping:true")
182             .run(context -> assertThat(
183                 context.getBean(SqlSessionFactory.class).getConfiguration().isArgNameBasedConstructorAutoMapping())
184                     .isTrue()),
185         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class)
186             .withPropertyValues("mybatis.configuration.lazy-loading-enabled:true")
187             .run(context -> assertThat(
188                 context.getBean(SqlSessionFactory.class).getConfiguration().isLazyLoadingEnabled()).isTrue()),
189         () -> this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).withPropertyValues(
190             "mybatis.configuration.default-statement-timeout:2000", "mybatis.configuration.default-fetch-size:1000",
191             "mybatis.configuration.local-cache-scope:STATEMENT", "mybatis.configuration.jdbc-type-for-null:NULL",
192             "mybatis.configuration.default-result-set-type:FORWARD_ONLY",
193             "mybatis.configuration.default-executor-type:BATCH", "mybatis.configuration.auto-mapping-behavior:FULL",
194             "mybatis.configuration.auto-mapping-unknown-column-behavior:WARNING",
195             "mybatis.configuration.log-prefix:[SQL]",
196             "mybatis.configuration.lazy-Load-trigger-methods:equals,clone,hashCode,toString,toDumpString",
197             "mybatis.configuration.logImpl:org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl",
198             "mybatis.configuration.vfsImpl:org.mybatis.spring.boot.autoconfigure.MybatisPropertiesTest$MyVFS",
199             "mybatis.configuration.default-sql-provider-type:org.mybatis.spring.boot.autoconfigure.MybatisPropertiesTest$MySqlProvider",
200             "mybatis.configuration.defaultEnumTypeHandler:org.apache.ibatis.type.EnumOrdinalTypeHandler",
201             "mybatis.configuration.configuration-factory:org.mybatis.spring.boot.autoconfigure.MybatisPropertiesTest$MyConfigurationFactory",
202             "mybatis.configuration.variables.key1:value1", "mybatis.configuration.variables.key2:value2",
203             "mybatis.configuration.database-id:mysql").run(context -> {
204               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultStatementTimeout())
205                   .isEqualTo(2000);
206               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultFetchSize())
207                   .isEqualTo(1000);
208               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLocalCacheScope())
209                   .isEqualTo(LocalCacheScope.STATEMENT);
210               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getJdbcTypeForNull())
211                   .isEqualTo(JdbcType.NULL);
212               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultResultSetType())
213                   .isEqualTo(ResultSetType.FORWARD_ONLY);
214               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultExecutorType())
215                   .isEqualTo(ExecutorType.BATCH);
216               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getAutoMappingBehavior())
217                   .isEqualTo(AutoMappingBehavior.FULL);
218               assertThat(
219                   context.getBean(SqlSessionFactory.class).getConfiguration().getAutoMappingUnknownColumnBehavior())
220                       .isEqualTo(AutoMappingUnknownColumnBehavior.WARNING);
221               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLogPrefix()).isEqualTo("[SQL]");
222               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLazyLoadTriggerMethods())
223                   .isEqualTo(new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString", "toDumpString")));
224               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getLogImpl())
225                   .isEqualTo(JakartaCommonsLoggingImpl.class);
226               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVfsImpl())
227                   .isEqualTo(MyVFS.class);
228               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDefaultSqlProviderType())
229                   .isEqualTo(MySqlProvider.class);
230               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getTypeHandlerRegistry()
231                   .getTypeHandler(JdbcType.class).getClass()).isEqualTo(EnumOrdinalTypeHandler.class);
232               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getConfigurationFactory())
233                   .isEqualTo(MyConfigurationFactory.class);
234               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVariables())
235                   .hasToString("{key1=value1, key2=value2}");
236               assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDatabaseId())
237                   .hasToString("mysql");
238             }));
239   }
240 
241   @Test
242   void checkProperties() {
243     Set<String> mybatisCoreConfigurationProperties = Arrays
244         .stream(PropertyAccessorFactory.forBeanPropertyAccess(new Configuration()).getPropertyDescriptors())
245         .map(PropertyDescriptor::getName).collect(Collectors.toSet());
246     Set<String> mybatisSpringBootConfigurationProperties = Arrays.stream(PropertyAccessorFactory
247         .forBeanPropertyAccess(new MybatisProperties.CoreConfiguration()).getPropertyDescriptors())
248         .map(PropertyDescriptor::getName).collect(Collectors.toSet());
249     mybatisCoreConfigurationProperties.removeAll(mybatisSpringBootConfigurationProperties);
250     mybatisCoreConfigurationProperties.removeAll(Arrays.asList("reflectorFactory", "defaultScriptingLanguage",
251         "sqlFragments", "typeHandlerRegistry", "mapperRegistry", "interceptors", "cacheNames", "incompleteResultMaps",
252         "typeAliasRegistry", "incompleteMethods", "proxyFactory", "resultMaps", "defaultScriptingLanguageInstance",
253         "parameterMaps", "keyGenerators", "parameterMapNames", "caches", "mappedStatementNames", "objectWrapperFactory",
254         "objectFactory", "incompleteStatements", "resultMapNames", "defaultScriptingLanuageInstance",
255         "keyGeneratorNames", "environment", "mappedStatements", "languageRegistry", "incompleteCacheRefs"));
256     assertThat(mybatisCoreConfigurationProperties).isEmpty();
257   }
258 
259   static class MyVFS extends SpringBootVFS {
260   }
261 
262   static class MySqlProvider {
263   }
264 
265   static class MyConfigurationFactory {
266   }
267 
268 }