diff --git a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java index 5da820faa..0987bb6f5 100644 --- a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java +++ b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.java @@ -38,6 +38,7 @@ import org.mybatis.spring.mapper.MapperScannerConfigurer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.BeanFactory; @@ -48,6 +49,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -56,18 +58,17 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; +import org.springframework.util.*; /** * {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a {@link SqlSessionFactory} and a @@ -81,245 +82,251 @@ * @author Eduardo Macarrón */ @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) -@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) +@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnSingleCandidate(DataSource.class) @EnableConfigurationProperties(MybatisProperties.class) -@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) +@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) public class MybatisAutoConfiguration implements InitializingBean { - private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); + private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); - private final MybatisProperties properties; + private final MybatisProperties properties; - private final Interceptor[] interceptors; + private final Interceptor[] interceptors; - private final TypeHandler[] typeHandlers; + private final TypeHandler[] typeHandlers; - private final LanguageDriver[] languageDrivers; + private final LanguageDriver[] languageDrivers; - private final ResourceLoader resourceLoader; + private final ResourceLoader resourceLoader; - private final DatabaseIdProvider databaseIdProvider; + private final DatabaseIdProvider databaseIdProvider; - private final List configurationCustomizers; + private final List configurationCustomizers; - private final List sqlSessionFactoryBeanCustomizers; + private final List sqlSessionFactoryBeanCustomizers; - public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider, - ObjectProvider typeHandlersProvider, ObjectProvider languageDriversProvider, - ResourceLoader resourceLoader, ObjectProvider databaseIdProvider, - ObjectProvider> configurationCustomizersProvider, - ObjectProvider> sqlSessionFactoryBeanCustomizers) { - this.properties = properties; - this.interceptors = interceptorsProvider.getIfAvailable(); - this.typeHandlers = typeHandlersProvider.getIfAvailable(); - this.languageDrivers = languageDriversProvider.getIfAvailable(); - this.resourceLoader = resourceLoader; - this.databaseIdProvider = databaseIdProvider.getIfAvailable(); - this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(); - this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable(); - } - - @Override - public void afterPropertiesSet() { - checkConfigFileExists(); - } - - private void checkConfigFileExists() { - if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) { - Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation()); - Assert.state(resource.exists(), - "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)"); - } - } - - @Bean - @ConditionalOnMissingBean - public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { - SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); - factory.setDataSource(dataSource); - if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) { - factory.setVfs(SpringBootVFS.class); - } - if (StringUtils.hasText(this.properties.getConfigLocation())) { - factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); - } - applyConfiguration(factory); - if (this.properties.getConfigurationProperties() != null) { - factory.setConfigurationProperties(this.properties.getConfigurationProperties()); - } - if (!ObjectUtils.isEmpty(this.interceptors)) { - factory.setPlugins(this.interceptors); - } - if (this.databaseIdProvider != null) { - factory.setDatabaseIdProvider(this.databaseIdProvider); - } - if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { - factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); - } - if (this.properties.getTypeAliasesSuperType() != null) { - factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); - } - if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { - factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); - } - if (!ObjectUtils.isEmpty(this.typeHandlers)) { - factory.setTypeHandlers(this.typeHandlers); - } - Resource[] mapperLocations = this.properties.resolveMapperLocations(); - if (!ObjectUtils.isEmpty(mapperLocations)) { - factory.setMapperLocations(mapperLocations); - } - Set factoryPropertyNames = Stream - .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName) - .collect(Collectors.toSet()); - Class defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); - if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) { - // Need to mybatis-spring 2.0.2+ - factory.setScriptingLanguageDrivers(this.languageDrivers); - if (defaultLanguageDriver == null && this.languageDrivers.length == 1) { - defaultLanguageDriver = this.languageDrivers[0].getClass(); - } - } - if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { - // Need to mybatis-spring 2.0.2+ - factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); - } - applySqlSessionFactoryBeanCustomizers(factory); - return factory.getObject(); - } - - private void applyConfiguration(SqlSessionFactoryBean factory) { - MybatisProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration(); - Configuration configuration = null; - if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) { - configuration = new Configuration(); + public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider, + ObjectProvider typeHandlersProvider, ObjectProvider languageDriversProvider, + ResourceLoader resourceLoader, ObjectProvider databaseIdProvider, + ObjectProvider> configurationCustomizersProvider, + ObjectProvider> sqlSessionFactoryBeanCustomizers) { + this.properties = properties; + this.interceptors = interceptorsProvider.getIfAvailable(); + this.typeHandlers = typeHandlersProvider.getIfAvailable(); + this.languageDrivers = languageDriversProvider.getIfAvailable(); + this.resourceLoader = resourceLoader; + this.databaseIdProvider = databaseIdProvider.getIfAvailable(); + this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(); + this.sqlSessionFactoryBeanCustomizers = sqlSessionFactoryBeanCustomizers.getIfAvailable(); } - if (configuration != null && coreConfiguration != null) { - coreConfiguration.applyTo(configuration); - } - if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { - for (ConfigurationCustomizer customizer : this.configurationCustomizers) { - customizer.customize(configuration); - } - } - factory.setConfiguration(configuration); - } - - private void applySqlSessionFactoryBeanCustomizers(SqlSessionFactoryBean factory) { - if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) { - for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) { - customizer.customize(factory); - } - } - } - - @Bean - @ConditionalOnMissingBean - public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { - ExecutorType executorType = this.properties.getExecutorType(); - if (executorType != null) { - return new SqlSessionTemplate(sqlSessionFactory, executorType); - } else { - return new SqlSessionTemplate(sqlSessionFactory); - } - } - - /** - * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use - * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box, - * similar to using Spring Data JPA repositories. - */ - public static class AutoConfiguredMapperScannerRegistrar - implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { - - private BeanFactory beanFactory; - private Environment environment; @Override - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { - - if (!AutoConfigurationPackages.has(this.beanFactory)) { - logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); - return; - } - - logger.debug("Searching for mappers annotated with @Mapper"); - - List packages = AutoConfigurationPackages.get(this.beanFactory); - if (logger.isDebugEnabled()) { - packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); - } - - BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); - builder.addPropertyValue("processPropertyPlaceHolders", true); - builder.addPropertyValue("annotationClass", Mapper.class); - builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); - BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); - Set propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName) - .collect(Collectors.toSet()); - if (propertyNames.contains("lazyInitialization")) { - // Need to mybatis-spring 2.0.2+ - builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); - } - if (propertyNames.contains("defaultScope")) { - // Need to mybatis-spring 2.0.6+ - builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); - } - - // for spring-native - boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, - Boolean.TRUE); - if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) { - ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; - Optional sqlSessionTemplateBeanName = Optional - .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory)); - Optional sqlSessionFactoryBeanName = Optional - .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory)); - if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) { - builder.addPropertyValue("sqlSessionTemplateBeanName", - sqlSessionTemplateBeanName.orElse("sqlSessionTemplate")); - } else { - builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.orElseThrow()); + public void afterPropertiesSet() { + checkConfigFileExists(); + } + + private void checkConfigFileExists() { + if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) { + Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation()); + Assert.state(resource.exists(), + "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)"); } - } - builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + } - registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); + @Bean + @ConditionalOnMissingBean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); + factory.setDataSource(dataSource); + if (properties.getConfiguration() == null || properties.getConfiguration().getVfsImpl() == null) { + factory.setVfs(SpringBootVFS.class); + } + if (StringUtils.hasText(this.properties.getConfigLocation())) { + factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation())); + } + applyConfiguration(factory); + if (this.properties.getConfigurationProperties() != null) { + factory.setConfigurationProperties(this.properties.getConfigurationProperties()); + } + if (!ObjectUtils.isEmpty(this.interceptors)) { + factory.setPlugins(this.interceptors); + } + if (this.databaseIdProvider != null) { + factory.setDatabaseIdProvider(this.databaseIdProvider); + } + if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) { + factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage()); + } + if (this.properties.getTypeAliasesSuperType() != null) { + factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType()); + } + if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) { + factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage()); + } + if (!ObjectUtils.isEmpty(this.typeHandlers)) { + factory.setTypeHandlers(this.typeHandlers); + } + Resource[] mapperLocations = this.properties.resolveMapperLocations(); + if (!ObjectUtils.isEmpty(mapperLocations)) { + factory.setMapperLocations(mapperLocations); + } + Set factoryPropertyNames = Stream + .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName) + .collect(Collectors.toSet()); + Class defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver(); + if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) { + // Need to mybatis-spring 2.0.2+ + factory.setScriptingLanguageDrivers(this.languageDrivers); + if (defaultLanguageDriver == null && this.languageDrivers.length == 1) { + defaultLanguageDriver = this.languageDrivers[0].getClass(); + } + } + if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { + // Need to mybatis-spring 2.0.2+ + factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver); + } + applySqlSessionFactoryBeanCustomizers(factory); + return factory.getObject(); } - @Override - public void setBeanFactory(BeanFactory beanFactory) { - this.beanFactory = beanFactory; + private void applyConfiguration(SqlSessionFactoryBean factory) { + MybatisProperties.CoreConfiguration coreConfiguration = this.properties.getConfiguration(); + Configuration configuration = null; + if (coreConfiguration != null || !StringUtils.hasText(this.properties.getConfigLocation())) { + configuration = new Configuration(); + } + if (configuration != null && coreConfiguration != null) { + coreConfiguration.applyTo(configuration); + } + if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) { + for (ConfigurationCustomizer customizer : this.configurationCustomizers) { + customizer.customize(configuration); + } + } + factory.setConfiguration(configuration); } - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; + private void applySqlSessionFactoryBeanCustomizers(SqlSessionFactoryBean factory) { + if (!CollectionUtils.isEmpty(this.sqlSessionFactoryBeanCustomizers)) { + for (SqlSessionFactoryBeanCustomizer customizer : this.sqlSessionFactoryBeanCustomizers) { + customizer.customize(factory); + } + } } - private String getBeanNameForType(Class type, ListableBeanFactory factory) { - String[] beanNames = factory.getBeanNamesForType(type); - return beanNames.length > 0 ? beanNames[0] : null; + @Bean + @ConditionalOnMissingBean + public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + ExecutorType executorType = this.properties.getExecutorType(); + if (executorType != null) { + return new SqlSessionTemplate(sqlSessionFactory, executorType); + } else { + return new SqlSessionTemplate(sqlSessionFactory); + } } - } + /** + * This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use + * {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box, + * similar to using Spring Data JPA repositories. + */ + public static class AutoConfiguredMapperScannerRegistrar + implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar { + + private BeanFactory beanFactory; + private Environment environment; + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + + if (!AutoConfigurationPackages.has(this.beanFactory)) { + logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); + return; + } + + logger.debug("Searching for mappers annotated with @Mapper"); + + List packages = AutoConfigurationPackages.get(this.beanFactory); + if (logger.isDebugEnabled()) { + packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); + } + + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class); + builder.addPropertyValue("processPropertyPlaceHolders", true); + builder.addPropertyValue("annotationClass", Mapper.class); + builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages)); + BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class); + Set propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName) + .collect(Collectors.toSet()); + if (propertyNames.contains("lazyInitialization")) { + // Need to mybatis-spring 2.0.2+ + builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}"); + } + if (propertyNames.contains("defaultScope")) { + // Need to mybatis-spring 2.0.6+ + builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}"); + } + + // for spring-native + boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class, + Boolean.TRUE); + if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) { + ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; + Optional sqlSessionTemplateBeanName = Optional + .ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory)); + Optional sqlSessionFactoryBeanName = Optional + .ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory)); + if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) { + builder.addPropertyValue("sqlSessionTemplateBeanName", + sqlSessionTemplateBeanName.orElse("sqlSessionTemplate")); + } else { + builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.orElseThrow()); + } + } + + Binder binder = Binder.get(environment); + MybatisProperties mybatisProperties = binder.bind(MybatisProperties.MYBATIS_PREFIX, MybatisProperties.class).orElseGet(MybatisProperties::new); + + Optional.ofNullable(mybatisProperties.getBeanNameGenerator()).ifPresent(beanNameGenerator -> builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(beanNameGenerator))); + + builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); + } - /** - * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan - * mappers based on the same component-scanning path as Spring Boot itself. - */ - @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) - @Import(AutoConfiguredMapperScannerRegistrar.class) - @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class }) - public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { + @Override + public void setBeanFactory(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + private String getBeanNameForType(Class type, ListableBeanFactory factory) { + String[] beanNames = factory.getBeanNamesForType(type); + return beanNames.length > 0 ? beanNames[0] : null; + } - @Override - public void afterPropertiesSet() { - logger.debug( - "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); } - } + /** + * If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan + * mappers based on the same component-scanning path as Spring Boot itself. + */ + @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) + @Import(AutoConfiguredMapperScannerRegistrar.class) + @ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class}) + public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean { + + @Override + public void afterPropertiesSet() { + logger.debug( + "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer."); + } + + } } diff --git a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisProperties.java b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisProperties.java index da6c41152..767de535e 100644 --- a/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisProperties.java +++ b/mybatis-spring-boot-autoconfigure/src/main/java/org/mybatis/spring/boot/autoconfigure/MybatisProperties.java @@ -32,6 +32,7 @@ import org.apache.ibatis.session.LocalCacheScope; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.TypeHandler; +import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.PropertyMapper; @@ -48,666 +49,680 @@ @ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX) public class MybatisProperties { - public static final String MYBATIS_PREFIX = "mybatis"; - - private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); - - /** - * Location of MyBatis xml config file. - */ - private String configLocation; - - /** - * Locations of MyBatis mapper files. - */ - private String[] mapperLocations; - - /** - * Packages to search type aliases. (Package delimiters are ",; \t\n") - */ - private String typeAliasesPackage; - - /** - * The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that - * searched from typeAliasesPackage. - */ - private Class typeAliasesSuperType; - - /** - * Packages to search for type handlers. (Package delimiters are ",; \t\n") - */ - private String typeHandlersPackage; - - /** - * Indicates whether perform presence check of the MyBatis xml config file. - */ - private boolean checkConfigLocation = false; - - /** - * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}. - */ - private ExecutorType executorType; - - /** - * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+) - */ - private Class defaultScriptingLanguageDriver; - - /** - * Externalized properties for MyBatis configuration. - */ - private Properties configurationProperties; - - /** - * A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is - * not used. - */ - private CoreConfiguration configuration; - - /** - * @since 1.1.0 - */ - public String getConfigLocation() { - return this.configLocation; - } - - /** - * @since 1.1.0 - */ - public void setConfigLocation(String configLocation) { - this.configLocation = configLocation; - } - - public String[] getMapperLocations() { - return this.mapperLocations; - } - - public void setMapperLocations(String[] mapperLocations) { - this.mapperLocations = mapperLocations; - } - - public String getTypeHandlersPackage() { - return this.typeHandlersPackage; - } - - public void setTypeHandlersPackage(String typeHandlersPackage) { - this.typeHandlersPackage = typeHandlersPackage; - } - - public String getTypeAliasesPackage() { - return this.typeAliasesPackage; - } - - public void setTypeAliasesPackage(String typeAliasesPackage) { - this.typeAliasesPackage = typeAliasesPackage; - } - - /** - * @since 1.3.3 - */ - public Class getTypeAliasesSuperType() { - return typeAliasesSuperType; - } - - /** - * @since 1.3.3 - */ - public void setTypeAliasesSuperType(Class typeAliasesSuperType) { - this.typeAliasesSuperType = typeAliasesSuperType; - } - - public boolean isCheckConfigLocation() { - return this.checkConfigLocation; - } - - public void setCheckConfigLocation(boolean checkConfigLocation) { - this.checkConfigLocation = checkConfigLocation; - } - - public ExecutorType getExecutorType() { - return this.executorType; - } - - public void setExecutorType(ExecutorType executorType) { - this.executorType = executorType; - } - - /** - * @since 2.1.0 - */ - public Class getDefaultScriptingLanguageDriver() { - return defaultScriptingLanguageDriver; - } - - /** - * @since 2.1.0 - */ - public void setDefaultScriptingLanguageDriver(Class defaultScriptingLanguageDriver) { - this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver; - } - - /** - * @since 1.2.0 - */ - public Properties getConfigurationProperties() { - return configurationProperties; - } - - /** - * @since 1.2.0 - */ - public void setConfigurationProperties(Properties configurationProperties) { - this.configurationProperties = configurationProperties; - } - - public CoreConfiguration getConfiguration() { - return configuration; - } - - public void setConfiguration(CoreConfiguration configuration) { - this.configuration = configuration; - } - - public Resource[] resolveMapperLocations() { - return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0])) - .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new); - } - - private Resource[] getResources(String location) { - try { - return resourceResolver.getResources(location); - } catch (IOException e) { - return new Resource[0]; - } - } + public static final String MYBATIS_PREFIX = "mybatis"; - /** - * The configuration properties for mybatis core module. - * - * @since 3.0.0 - */ - public static class CoreConfiguration { + private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); /** - * Allows using RowBounds on nested statements. If allow, set the false. Default is false. + * Location of MyBatis xml config file. */ - private Boolean safeRowBoundsEnabled; + private String configLocation; /** - * Allows using ResultHandler on nested statements. If allow, set the false. Default is true. + * Locations of MyBatis mapper files. */ - private Boolean safeResultHandlerEnabled; + private String[] mapperLocations; /** - * Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names - * aColumn. Default is false. + * Packages to search type aliases. (Package delimiters are ",; \t\n") */ - private Boolean mapUnderscoreToCamelCase; + private String typeAliasesPackage; /** - * When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded - * on demand (see also lazyLoadTriggerMethods). Default is false. + * The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that + * searched from typeAliasesPackage. */ - private Boolean aggressiveLazyLoading; + private Class typeAliasesSuperType; /** - * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required). - * Default is true. + * Packages to search for type handlers. (Package delimiters are ",; \t\n") */ - private Boolean multipleResultSetsEnabled; + private String typeHandlersPackage; /** - * Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be - * used if set to true, as some drivers deny compatibility but still work (e.g. Derby). Default is false. + * Indicates whether perform presence check of the MyBatis xml config file. */ - private Boolean useGeneratedKeys; + private boolean checkConfigLocation = false; /** - * Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to - * the driver documentation, or test out both modes to determine how your driver behaves. Default is true. + * Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}. */ - private Boolean useColumnLabel; + private ExecutorType executorType; /** - * Globally enables or disables any caches configured in any mapper under this configuration. Default is true. + * The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+) */ - private Boolean cacheEnabled; + private Class defaultScriptingLanguageDriver; /** - * Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you - * rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to - * null. Default is false. + * Externalized properties for MyBatis configuration. */ - private Boolean callSettersOnNulls; + private Properties configurationProperties; /** - * Allow referencing statement parameters by their actual names declared in the method signature. To use this - * feature, your project must be compiled in Java 8 with -parameters option. Default is true. + * A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is + * not used. */ - private Boolean useActualParamName; - - /** - * MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, - * MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collection and - * association). Default is false. - */ - private Boolean returnInstanceForEmptyRow; - - /** - * Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. Default is - * false. - */ - private Boolean shrinkWhitespacesInSql; - - /** - * Specifies the default value of 'nullable' attribute on 'foreach' tag. Default is false. - */ - private Boolean nullableOnForEach; - - /** - * When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on - * the column order. Default is false. - */ - private Boolean argNameBasedConstructorAutoMapping; - - /** - * Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be - * superseded for a specific relation by using the fetchType attribute on it. Default is False. - */ - private Boolean lazyLoadingEnabled; - + private CoreConfiguration configuration; /** - * Sets the number of seconds the driver will wait for a response from the database. + * The customize Mapper bean name generator class + * + * @see org.mybatis.spring.mapper.MapperScannerConfigurer#setNameGenerator(BeanNameGenerator) */ - private Integer defaultStatementTimeout; + private Class beanNameGenerator; /** - * Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a - * query setting. + * @since 1.1.0 */ - private Integer defaultFetchSize; - - /** - * MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default - * (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be - * used just for statement execution, no data will be shared between two different calls to the same SqlSession. - * Default is SESSION. - */ - private LocalCacheScope localCacheScope; - - /** - * Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers - * require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. Default - * is OTHER. - */ - private JdbcType jdbcTypeForNull; - - /** - * Specifies a scroll strategy when omit it per statement settings. - */ - private ResultSetType defaultResultSetType; - - /** - * Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements. - * BATCH executor reuses statements and batches updates. Default is SIMPLE. - */ - private ExecutorType defaultExecutorType; - - /** - * Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping. - * PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result - * mappings of any complexity (containing nested or otherwise). Default is PARTIAL. - */ - private AutoMappingBehavior autoMappingBehavior; - - /** - * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. - * Default is NONE. - */ - private AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior; - - /** - * Specifies the prefix string that MyBatis will add to the logger names. - */ - private String logPrefix; - - /** - * Specifies which Object's methods trigger a lazy load. Default is [equals,clone,hashCode,toString]. - */ - private Set lazyLoadTriggerMethods; - - /** - * Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation - * will be autodiscovered. - */ - private Class logImpl; - - /** - * Specifies VFS implementations. - */ - private Class vfsImpl; - - /** - * Specifies an sql provider class that holds provider method. This class apply to the type(or value) attribute on - * sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. - */ - private Class defaultSqlProviderType; - - /** - * Specifies the TypeHandler used by default for Enum. - */ - Class defaultEnumTypeHandler; - - /** - * Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to - * load lazy properties of deserialized objects. This class must have a method with a signature static Configuration - * getConfiguration(). - */ - private Class configurationFactory; - - /** - * Specify any configuration variables. - */ - private Properties variables; + public String getConfigLocation() { + return this.configLocation; + } /** - * Specifies the database identify value for switching query to use. + * @since 1.1.0 */ - private String databaseId; - - public Boolean getSafeRowBoundsEnabled() { - return safeRowBoundsEnabled; - } - - public void setSafeRowBoundsEnabled(Boolean safeRowBoundsEnabled) { - this.safeRowBoundsEnabled = safeRowBoundsEnabled; - } - - public Boolean getSafeResultHandlerEnabled() { - return safeResultHandlerEnabled; + public void setConfigLocation(String configLocation) { + this.configLocation = configLocation; } - public void setSafeResultHandlerEnabled(Boolean safeResultHandlerEnabled) { - this.safeResultHandlerEnabled = safeResultHandlerEnabled; + public String[] getMapperLocations() { + return this.mapperLocations; } - public Boolean getMapUnderscoreToCamelCase() { - return mapUnderscoreToCamelCase; + public void setMapperLocations(String[] mapperLocations) { + this.mapperLocations = mapperLocations; } - public void setMapUnderscoreToCamelCase(Boolean mapUnderscoreToCamelCase) { - this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase; + public String getTypeHandlersPackage() { + return this.typeHandlersPackage; } - public Boolean getAggressiveLazyLoading() { - return aggressiveLazyLoading; + public void setTypeHandlersPackage(String typeHandlersPackage) { + this.typeHandlersPackage = typeHandlersPackage; } - public void setAggressiveLazyLoading(Boolean aggressiveLazyLoading) { - this.aggressiveLazyLoading = aggressiveLazyLoading; + public String getTypeAliasesPackage() { + return this.typeAliasesPackage; } - @DeprecatedConfigurationProperty(since = "3.0.4", reason = "The option is not used at MyBatis core module. It will be removed in the future. See https://github.com/mybatis/mybatis-3/pull/3238") - public Boolean getMultipleResultSetsEnabled() { - return multipleResultSetsEnabled; + public void setTypeAliasesPackage(String typeAliasesPackage) { + this.typeAliasesPackage = typeAliasesPackage; } - public void setMultipleResultSetsEnabled(Boolean multipleResultSetsEnabled) { - this.multipleResultSetsEnabled = multipleResultSetsEnabled; - } - - public Boolean getUseGeneratedKeys() { - return useGeneratedKeys; - } - - public void setUseGeneratedKeys(Boolean useGeneratedKeys) { - this.useGeneratedKeys = useGeneratedKeys; - } - - public Boolean getUseColumnLabel() { - return useColumnLabel; - } - - public void setUseColumnLabel(Boolean useColumnLabel) { - this.useColumnLabel = useColumnLabel; - } - - public Boolean getCacheEnabled() { - return cacheEnabled; - } - - public void setCacheEnabled(Boolean cacheEnabled) { - this.cacheEnabled = cacheEnabled; - } - - public Boolean getCallSettersOnNulls() { - return callSettersOnNulls; - } - - public void setCallSettersOnNulls(Boolean callSettersOnNulls) { - this.callSettersOnNulls = callSettersOnNulls; - } - - public Boolean getUseActualParamName() { - return useActualParamName; - } - - public void setUseActualParamName(Boolean useActualParamName) { - this.useActualParamName = useActualParamName; + /** + * @since 1.3.3 + */ + public Class getTypeAliasesSuperType() { + return typeAliasesSuperType; } - public Boolean getReturnInstanceForEmptyRow() { - return returnInstanceForEmptyRow; + /** + * @since 1.3.3 + */ + public void setTypeAliasesSuperType(Class typeAliasesSuperType) { + this.typeAliasesSuperType = typeAliasesSuperType; } - public void setReturnInstanceForEmptyRow(Boolean returnInstanceForEmptyRow) { - this.returnInstanceForEmptyRow = returnInstanceForEmptyRow; + public boolean isCheckConfigLocation() { + return this.checkConfigLocation; } - public Boolean getShrinkWhitespacesInSql() { - return shrinkWhitespacesInSql; + public void setCheckConfigLocation(boolean checkConfigLocation) { + this.checkConfigLocation = checkConfigLocation; } - public void setShrinkWhitespacesInSql(Boolean shrinkWhitespacesInSql) { - this.shrinkWhitespacesInSql = shrinkWhitespacesInSql; + public ExecutorType getExecutorType() { + return this.executorType; } - public Boolean getNullableOnForEach() { - return nullableOnForEach; + public void setExecutorType(ExecutorType executorType) { + this.executorType = executorType; } - public void setNullableOnForEach(Boolean nullableOnForEach) { - this.nullableOnForEach = nullableOnForEach; + /** + * @since 2.1.0 + */ + public Class getDefaultScriptingLanguageDriver() { + return defaultScriptingLanguageDriver; } - public Boolean getArgNameBasedConstructorAutoMapping() { - return argNameBasedConstructorAutoMapping; + /** + * @since 2.1.0 + */ + public void setDefaultScriptingLanguageDriver(Class defaultScriptingLanguageDriver) { + this.defaultScriptingLanguageDriver = defaultScriptingLanguageDriver; } - public void setArgNameBasedConstructorAutoMapping(Boolean argNameBasedConstructorAutoMapping) { - this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping; + /** + * @since 1.2.0 + */ + public Properties getConfigurationProperties() { + return configurationProperties; } - public String getLogPrefix() { - return logPrefix; + /** + * @since 1.2.0 + */ + public void setConfigurationProperties(Properties configurationProperties) { + this.configurationProperties = configurationProperties; } - public void setLogPrefix(String logPrefix) { - this.logPrefix = logPrefix; + public CoreConfiguration getConfiguration() { + return configuration; } - public Class getLogImpl() { - return logImpl; + public void setConfiguration(CoreConfiguration configuration) { + this.configuration = configuration; } - public void setLogImpl(Class logImpl) { - this.logImpl = logImpl; + public Class getBeanNameGenerator() { + return beanNameGenerator; } - public Class getVfsImpl() { - return vfsImpl; + public void setBeanNameGenerator(Class beanNameGenerator) { + this.beanNameGenerator = beanNameGenerator; } - public void setVfsImpl(Class vfsImpl) { - this.vfsImpl = vfsImpl; + public Resource[] resolveMapperLocations() { + return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0])) + .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new); } - public Class getDefaultSqlProviderType() { - return defaultSqlProviderType; + private Resource[] getResources(String location) { + try { + return resourceResolver.getResources(location); + } catch (IOException e) { + return new Resource[0]; + } } - public void setDefaultSqlProviderType(Class defaultSqlProviderType) { - this.defaultSqlProviderType = defaultSqlProviderType; - } + /** + * The configuration properties for mybatis core module. + * + * @since 3.0.0 + */ + public static class CoreConfiguration { - public LocalCacheScope getLocalCacheScope() { - return localCacheScope; - } + /** + * Allows using RowBounds on nested statements. If allow, set the false. Default is false. + */ + private Boolean safeRowBoundsEnabled; - public void setLocalCacheScope(LocalCacheScope localCacheScope) { - this.localCacheScope = localCacheScope; - } + /** + * Allows using ResultHandler on nested statements. If allow, set the false. Default is true. + */ + private Boolean safeResultHandlerEnabled; - public JdbcType getJdbcTypeForNull() { - return jdbcTypeForNull; - } + /** + * Enables automatic mapping from classic database column names A_COLUMN to camel case classic Java property names + * aColumn. Default is false. + */ + private Boolean mapUnderscoreToCamelCase; - public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) { - this.jdbcTypeForNull = jdbcTypeForNull; - } + /** + * When enabled, any method call will load all the lazy properties of the object. Otherwise, each property is loaded + * on demand (see also lazyLoadTriggerMethods). Default is false. + */ + private Boolean aggressiveLazyLoading; - public Set getLazyLoadTriggerMethods() { - return lazyLoadTriggerMethods; - } + /** + * Allows or disallows multiple ResultSets to be returned from a single statement (compatible driver required). + * Default is true. + */ + private Boolean multipleResultSetsEnabled; - public void setLazyLoadTriggerMethods(Set lazyLoadTriggerMethods) { - this.lazyLoadTriggerMethods = lazyLoadTriggerMethods; - } + /** + * Allows JDBC support for generated keys. A compatible driver is required. This setting forces generated keys to be + * used if set to true, as some drivers deny compatibility but still work (e.g. Derby). Default is false. + */ + private Boolean useGeneratedKeys; - public Integer getDefaultStatementTimeout() { - return defaultStatementTimeout; - } + /** + * Uses the column label instead of the column name. Different drivers behave differently in this respect. Refer to + * the driver documentation, or test out both modes to determine how your driver behaves. Default is true. + */ + private Boolean useColumnLabel; - public void setDefaultStatementTimeout(Integer defaultStatementTimeout) { - this.defaultStatementTimeout = defaultStatementTimeout; - } + /** + * Globally enables or disables any caches configured in any mapper under this configuration. Default is true. + */ + private Boolean cacheEnabled; - public Integer getDefaultFetchSize() { - return defaultFetchSize; - } + /** + * Specifies if setters or map's put method will be called when a retrieved value is null. It is useful when you + * rely on Map.keySet() or null value initialization. Note primitives such as (int,boolean,etc.) will not be set to + * null. Default is false. + */ + private Boolean callSettersOnNulls; + + /** + * Allow referencing statement parameters by their actual names declared in the method signature. To use this + * feature, your project must be compiled in Java 8 with -parameters option. Default is true. + */ + private Boolean useActualParamName; + + /** + * MyBatis, by default, returns null when all the columns of a returned row are NULL. When this setting is enabled, + * MyBatis returns an empty instance instead. Note that it is also applied to nested results (i.e. collection and + * association). Default is false. + */ + private Boolean returnInstanceForEmptyRow; + + /** + * Removes extra whitespace characters from the SQL. Note that this also affects literal strings in SQL. Default is + * false. + */ + private Boolean shrinkWhitespacesInSql; + + /** + * Specifies the default value of 'nullable' attribute on 'foreach' tag. Default is false. + */ + private Boolean nullableOnForEach; + + /** + * When applying constructor auto-mapping, argument name is used to search the column to map instead of relying on + * the column order. Default is false. + */ + private Boolean argNameBasedConstructorAutoMapping; + + /** + * Globally enables or disables lazy loading. When enabled, all relations will be lazily loaded. This value can be + * superseded for a specific relation by using the fetchType attribute on it. Default is False. + */ + private Boolean lazyLoadingEnabled; + + /** + * Sets the number of seconds the driver will wait for a response from the database. + */ + private Integer defaultStatementTimeout; + + /** + * Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a + * query setting. + */ + private Integer defaultFetchSize; + + /** + * MyBatis uses local cache to prevent circular references and speed up repeated nested queries. By default + * (SESSION) all queries executed during a session are cached. If localCacheScope=STATEMENT local session will be + * used just for statement execution, no data will be shared between two different calls to the same SqlSession. + * Default is SESSION. + */ + private LocalCacheScope localCacheScope; + + /** + * Specifies the JDBC type for null values when no specific JDBC type was provided for the parameter. Some drivers + * require specifying the column JDBC type but others work with generic values like NULL, VARCHAR or OTHER. Default + * is OTHER. + */ + private JdbcType jdbcTypeForNull; + + /** + * Specifies a scroll strategy when omit it per statement settings. + */ + private ResultSetType defaultResultSetType; + + /** + * Configures the default executor. SIMPLE executor does nothing special. REUSE executor reuses prepared statements. + * BATCH executor reuses statements and batches updates. Default is SIMPLE. + */ + private ExecutorType defaultExecutorType; + + /** + * Specifies if and how MyBatis should automatically map columns to fields/properties. NONE disables auto-mapping. + * PARTIAL will only auto-map results with no nested result mappings defined inside. FULL will auto-map result + * mappings of any complexity (containing nested or otherwise). Default is PARTIAL. + */ + private AutoMappingBehavior autoMappingBehavior; + + /** + * Specify the behavior when detects an unknown column (or unknown property type) of automatic mapping target. + * Default is NONE. + */ + private AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior; + + /** + * Specifies the prefix string that MyBatis will add to the logger names. + */ + private String logPrefix; + + /** + * Specifies which Object's methods trigger a lazy load. Default is [equals,clone,hashCode,toString]. + */ + private Set lazyLoadTriggerMethods; + + /** + * Specifies which logging implementation MyBatis should use. If this setting is not present logging implementation + * will be autodiscovered. + */ + private Class logImpl; + + /** + * Specifies VFS implementations. + */ + private Class vfsImpl; + + /** + * Specifies an sql provider class that holds provider method. This class apply to the type(or value) attribute on + * sql provider annotation(e.g. @SelectProvider), when these attribute was omitted. + */ + private Class defaultSqlProviderType; + + /** + * Specifies the TypeHandler used by default for Enum. + */ + Class defaultEnumTypeHandler; + + /** + * Specifies the class that provides an instance of Configuration. The returned Configuration instance is used to + * load lazy properties of deserialized objects. This class must have a method with a signature static Configuration + * getConfiguration(). + */ + private Class configurationFactory; + + /** + * Specify any configuration variables. + */ + private Properties variables; + + /** + * Specifies the database identify value for switching query to use. + */ + private String databaseId; + + public Boolean getSafeRowBoundsEnabled() { + return safeRowBoundsEnabled; + } + + public void setSafeRowBoundsEnabled(Boolean safeRowBoundsEnabled) { + this.safeRowBoundsEnabled = safeRowBoundsEnabled; + } + + public Boolean getSafeResultHandlerEnabled() { + return safeResultHandlerEnabled; + } + + public void setSafeResultHandlerEnabled(Boolean safeResultHandlerEnabled) { + this.safeResultHandlerEnabled = safeResultHandlerEnabled; + } + + public Boolean getMapUnderscoreToCamelCase() { + return mapUnderscoreToCamelCase; + } + + public void setMapUnderscoreToCamelCase(Boolean mapUnderscoreToCamelCase) { + this.mapUnderscoreToCamelCase = mapUnderscoreToCamelCase; + } + + public Boolean getAggressiveLazyLoading() { + return aggressiveLazyLoading; + } + + public void setAggressiveLazyLoading(Boolean aggressiveLazyLoading) { + this.aggressiveLazyLoading = aggressiveLazyLoading; + } + + @DeprecatedConfigurationProperty(since = "3.0.4", reason = "The option is not used at MyBatis core module. It will be removed in the future. See https://github.com/mybatis/mybatis-3/pull/3238") + public Boolean getMultipleResultSetsEnabled() { + return multipleResultSetsEnabled; + } + + public void setMultipleResultSetsEnabled(Boolean multipleResultSetsEnabled) { + this.multipleResultSetsEnabled = multipleResultSetsEnabled; + } + + public Boolean getUseGeneratedKeys() { + return useGeneratedKeys; + } + + public void setUseGeneratedKeys(Boolean useGeneratedKeys) { + this.useGeneratedKeys = useGeneratedKeys; + } + + public Boolean getUseColumnLabel() { + return useColumnLabel; + } + + public void setUseColumnLabel(Boolean useColumnLabel) { + this.useColumnLabel = useColumnLabel; + } + + public Boolean getCacheEnabled() { + return cacheEnabled; + } + + public void setCacheEnabled(Boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + } + + public Boolean getCallSettersOnNulls() { + return callSettersOnNulls; + } + + public void setCallSettersOnNulls(Boolean callSettersOnNulls) { + this.callSettersOnNulls = callSettersOnNulls; + } + + public Boolean getUseActualParamName() { + return useActualParamName; + } + + public void setUseActualParamName(Boolean useActualParamName) { + this.useActualParamName = useActualParamName; + } + + public Boolean getReturnInstanceForEmptyRow() { + return returnInstanceForEmptyRow; + } + + public void setReturnInstanceForEmptyRow(Boolean returnInstanceForEmptyRow) { + this.returnInstanceForEmptyRow = returnInstanceForEmptyRow; + } - public void setDefaultFetchSize(Integer defaultFetchSize) { - this.defaultFetchSize = defaultFetchSize; - } + public Boolean getShrinkWhitespacesInSql() { + return shrinkWhitespacesInSql; + } - public ResultSetType getDefaultResultSetType() { - return defaultResultSetType; - } + public void setShrinkWhitespacesInSql(Boolean shrinkWhitespacesInSql) { + this.shrinkWhitespacesInSql = shrinkWhitespacesInSql; + } - public void setDefaultResultSetType(ResultSetType defaultResultSetType) { - this.defaultResultSetType = defaultResultSetType; - } + public Boolean getNullableOnForEach() { + return nullableOnForEach; + } - public ExecutorType getDefaultExecutorType() { - return defaultExecutorType; - } + public void setNullableOnForEach(Boolean nullableOnForEach) { + this.nullableOnForEach = nullableOnForEach; + } - public void setDefaultExecutorType(ExecutorType defaultExecutorType) { - this.defaultExecutorType = defaultExecutorType; - } + public Boolean getArgNameBasedConstructorAutoMapping() { + return argNameBasedConstructorAutoMapping; + } - public AutoMappingBehavior getAutoMappingBehavior() { - return autoMappingBehavior; - } + public void setArgNameBasedConstructorAutoMapping(Boolean argNameBasedConstructorAutoMapping) { + this.argNameBasedConstructorAutoMapping = argNameBasedConstructorAutoMapping; + } - public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { - this.autoMappingBehavior = autoMappingBehavior; - } + public String getLogPrefix() { + return logPrefix; + } - public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { - return autoMappingUnknownColumnBehavior; - } + public void setLogPrefix(String logPrefix) { + this.logPrefix = logPrefix; + } - public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { - this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; - } + public Class getLogImpl() { + return logImpl; + } - public Properties getVariables() { - return variables; - } + public void setLogImpl(Class logImpl) { + this.logImpl = logImpl; + } - public void setVariables(Properties variables) { - this.variables = variables; - } + public Class getVfsImpl() { + return vfsImpl; + } - public Boolean getLazyLoadingEnabled() { - return lazyLoadingEnabled; - } + public void setVfsImpl(Class vfsImpl) { + this.vfsImpl = vfsImpl; + } - public void setLazyLoadingEnabled(Boolean lazyLoadingEnabled) { - this.lazyLoadingEnabled = lazyLoadingEnabled; - } + public Class getDefaultSqlProviderType() { + return defaultSqlProviderType; + } - public Class getConfigurationFactory() { - return configurationFactory; - } + public void setDefaultSqlProviderType(Class defaultSqlProviderType) { + this.defaultSqlProviderType = defaultSqlProviderType; + } - public void setConfigurationFactory(Class configurationFactory) { - this.configurationFactory = configurationFactory; - } + public LocalCacheScope getLocalCacheScope() { + return localCacheScope; + } - public Class getDefaultEnumTypeHandler() { - return defaultEnumTypeHandler; - } + public void setLocalCacheScope(LocalCacheScope localCacheScope) { + this.localCacheScope = localCacheScope; + } - public void setDefaultEnumTypeHandler(Class defaultEnumTypeHandler) { - this.defaultEnumTypeHandler = defaultEnumTypeHandler; - } + public JdbcType getJdbcTypeForNull() { + return jdbcTypeForNull; + } - public String getDatabaseId() { - return databaseId; - } + public void setJdbcTypeForNull(JdbcType jdbcTypeForNull) { + this.jdbcTypeForNull = jdbcTypeForNull; + } - public void setDatabaseId(String databaseId) { - this.databaseId = databaseId; - } + public Set getLazyLoadTriggerMethods() { + return lazyLoadTriggerMethods; + } + + public void setLazyLoadTriggerMethods(Set lazyLoadTriggerMethods) { + this.lazyLoadTriggerMethods = lazyLoadTriggerMethods; + } + + public Integer getDefaultStatementTimeout() { + return defaultStatementTimeout; + } + + public void setDefaultStatementTimeout(Integer defaultStatementTimeout) { + this.defaultStatementTimeout = defaultStatementTimeout; + } + + public Integer getDefaultFetchSize() { + return defaultFetchSize; + } + + public void setDefaultFetchSize(Integer defaultFetchSize) { + this.defaultFetchSize = defaultFetchSize; + } + + public ResultSetType getDefaultResultSetType() { + return defaultResultSetType; + } + + public void setDefaultResultSetType(ResultSetType defaultResultSetType) { + this.defaultResultSetType = defaultResultSetType; + } + + public ExecutorType getDefaultExecutorType() { + return defaultExecutorType; + } + + public void setDefaultExecutorType(ExecutorType defaultExecutorType) { + this.defaultExecutorType = defaultExecutorType; + } + + public AutoMappingBehavior getAutoMappingBehavior() { + return autoMappingBehavior; + } + + public void setAutoMappingBehavior(AutoMappingBehavior autoMappingBehavior) { + this.autoMappingBehavior = autoMappingBehavior; + } + + public AutoMappingUnknownColumnBehavior getAutoMappingUnknownColumnBehavior() { + return autoMappingUnknownColumnBehavior; + } + + public void setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior) { + this.autoMappingUnknownColumnBehavior = autoMappingUnknownColumnBehavior; + } + + public Properties getVariables() { + return variables; + } + + public void setVariables(Properties variables) { + this.variables = variables; + } + + public Boolean getLazyLoadingEnabled() { + return lazyLoadingEnabled; + } + + public void setLazyLoadingEnabled(Boolean lazyLoadingEnabled) { + this.lazyLoadingEnabled = lazyLoadingEnabled; + } + + public Class getConfigurationFactory() { + return configurationFactory; + } + + public void setConfigurationFactory(Class configurationFactory) { + this.configurationFactory = configurationFactory; + } + + public Class getDefaultEnumTypeHandler() { + return defaultEnumTypeHandler; + } + + public void setDefaultEnumTypeHandler(Class defaultEnumTypeHandler) { + this.defaultEnumTypeHandler = defaultEnumTypeHandler; + } + + public String getDatabaseId() { + return databaseId; + } + + public void setDatabaseId(String databaseId) { + this.databaseId = databaseId; + } + + public void applyTo(Configuration target) { + PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); + mapper.from(getSafeRowBoundsEnabled()).to(target::setSafeRowBoundsEnabled); + mapper.from(getSafeResultHandlerEnabled()).to(target::setSafeResultHandlerEnabled); + mapper.from(getMapUnderscoreToCamelCase()).to(target::setMapUnderscoreToCamelCase); + mapper.from(getAggressiveLazyLoading()).to(target::setAggressiveLazyLoading); + mapper.from(getMultipleResultSetsEnabled()).to(target::setMultipleResultSetsEnabled); + mapper.from(getUseGeneratedKeys()).to(target::setUseGeneratedKeys); + mapper.from(getUseColumnLabel()).to(target::setUseColumnLabel); + mapper.from(getCacheEnabled()).to(target::setCacheEnabled); + mapper.from(getCallSettersOnNulls()).to(target::setCallSettersOnNulls); + mapper.from(getUseActualParamName()).to(target::setUseActualParamName); + mapper.from(getReturnInstanceForEmptyRow()).to(target::setReturnInstanceForEmptyRow); + mapper.from(getShrinkWhitespacesInSql()).to(target::setShrinkWhitespacesInSql); + mapper.from(getNullableOnForEach()).to(target::setNullableOnForEach); + mapper.from(getArgNameBasedConstructorAutoMapping()).to(target::setArgNameBasedConstructorAutoMapping); + mapper.from(getLazyLoadingEnabled()).to(target::setLazyLoadingEnabled); + mapper.from(getLogPrefix()).to(target::setLogPrefix); + mapper.from(getLazyLoadTriggerMethods()).to(target::setLazyLoadTriggerMethods); + mapper.from(getDefaultStatementTimeout()).to(target::setDefaultStatementTimeout); + mapper.from(getDefaultFetchSize()).to(target::setDefaultFetchSize); + mapper.from(getLocalCacheScope()).to(target::setLocalCacheScope); + mapper.from(getJdbcTypeForNull()).to(target::setJdbcTypeForNull); + mapper.from(getDefaultResultSetType()).to(target::setDefaultResultSetType); + mapper.from(getDefaultExecutorType()).to(target::setDefaultExecutorType); + mapper.from(getAutoMappingBehavior()).to(target::setAutoMappingBehavior); + mapper.from(getAutoMappingUnknownColumnBehavior()).to(target::setAutoMappingUnknownColumnBehavior); + mapper.from(getVariables()).to(target::setVariables); + mapper.from(getLogImpl()).to(target::setLogImpl); + mapper.from(getVfsImpl()).to(target::setVfsImpl); + mapper.from(getDefaultSqlProviderType()).to(target::setDefaultSqlProviderType); + mapper.from(getConfigurationFactory()).to(target::setConfigurationFactory); + mapper.from(getDefaultEnumTypeHandler()).to(target::setDefaultEnumTypeHandler); + mapper.from(getDatabaseId()).to(target::setDatabaseId); + } - public void applyTo(Configuration target) { - PropertyMapper mapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); - mapper.from(getSafeRowBoundsEnabled()).to(target::setSafeRowBoundsEnabled); - mapper.from(getSafeResultHandlerEnabled()).to(target::setSafeResultHandlerEnabled); - mapper.from(getMapUnderscoreToCamelCase()).to(target::setMapUnderscoreToCamelCase); - mapper.from(getAggressiveLazyLoading()).to(target::setAggressiveLazyLoading); - mapper.from(getMultipleResultSetsEnabled()).to(target::setMultipleResultSetsEnabled); - mapper.from(getUseGeneratedKeys()).to(target::setUseGeneratedKeys); - mapper.from(getUseColumnLabel()).to(target::setUseColumnLabel); - mapper.from(getCacheEnabled()).to(target::setCacheEnabled); - mapper.from(getCallSettersOnNulls()).to(target::setCallSettersOnNulls); - mapper.from(getUseActualParamName()).to(target::setUseActualParamName); - mapper.from(getReturnInstanceForEmptyRow()).to(target::setReturnInstanceForEmptyRow); - mapper.from(getShrinkWhitespacesInSql()).to(target::setShrinkWhitespacesInSql); - mapper.from(getNullableOnForEach()).to(target::setNullableOnForEach); - mapper.from(getArgNameBasedConstructorAutoMapping()).to(target::setArgNameBasedConstructorAutoMapping); - mapper.from(getLazyLoadingEnabled()).to(target::setLazyLoadingEnabled); - mapper.from(getLogPrefix()).to(target::setLogPrefix); - mapper.from(getLazyLoadTriggerMethods()).to(target::setLazyLoadTriggerMethods); - mapper.from(getDefaultStatementTimeout()).to(target::setDefaultStatementTimeout); - mapper.from(getDefaultFetchSize()).to(target::setDefaultFetchSize); - mapper.from(getLocalCacheScope()).to(target::setLocalCacheScope); - mapper.from(getJdbcTypeForNull()).to(target::setJdbcTypeForNull); - mapper.from(getDefaultResultSetType()).to(target::setDefaultResultSetType); - mapper.from(getDefaultExecutorType()).to(target::setDefaultExecutorType); - mapper.from(getAutoMappingBehavior()).to(target::setAutoMappingBehavior); - mapper.from(getAutoMappingUnknownColumnBehavior()).to(target::setAutoMappingUnknownColumnBehavior); - mapper.from(getVariables()).to(target::setVariables); - mapper.from(getLogImpl()).to(target::setLogImpl); - mapper.from(getVfsImpl()).to(target::setVfsImpl); - mapper.from(getDefaultSqlProviderType()).to(target::setDefaultSqlProviderType); - mapper.from(getConfigurationFactory()).to(target::setConfigurationFactory); - mapper.from(getDefaultEnumTypeHandler()).to(target::setDefaultEnumTypeHandler); - mapper.from(getDatabaseId()).to(target::setDatabaseId); } - } - } diff --git a/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/one/DateTimeMapper.java b/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/one/DateTimeMapper.java new file mode 100644 index 000000000..3ab3da3de --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/one/DateTimeMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.mapper.one; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; + +@Mapper +public interface DateTimeMapper { + + @Select("SELECT current_timestamp") + LocalDateTime now(); + +} diff --git a/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/two/DateTimeMapper.java b/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/two/DateTimeMapper.java new file mode 100644 index 000000000..821737a6f --- /dev/null +++ b/mybatis-spring-boot-autoconfigure/src/test/java/com/example/mapper/two/DateTimeMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.mapper.two; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.time.LocalDateTime; + +@Mapper +public interface DateTimeMapper { + + @Select("SELECT current_timestamp") + LocalDateTime now(); + +} diff --git a/mybatis-spring-boot-autoconfigure/src/test/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfigurationTest.java b/mybatis-spring-boot-autoconfigure/src/test/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfigurationTest.java index 77a8e413d..ce9b1c6a4 100644 --- a/mybatis-spring-boot-autoconfigure/src/test/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfigurationTest.java +++ b/mybatis-spring-boot-autoconfigure/src/test/java/org/mybatis/spring/boot/autoconfigure/MybatisAutoConfigurationTest.java @@ -81,9 +81,7 @@ import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.*; import org.springframework.context.support.SimpleThreadScope; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -99,1106 +97,1122 @@ */ class MybatisAutoConfigurationTest { - private ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(MybatisAutoConfiguration.class)); - - @Test - void testNoDataSource() { - this.contextRunner.withUserConfiguration(PropertyPlaceholderAutoConfiguration.class).run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).isEmpty(); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).isEmpty(); - assertThat(context.getBeanNamesForType(MybatisProperties.class)).isEmpty(); - }); - } - - @Test - void testMultipleDataSource() { - this.contextRunner - .withUserConfiguration(MultipleDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).isEmpty(); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).isEmpty(); - assertThat(context.getBeanNamesForType(MybatisProperties.class)).isEmpty(); - }); - } - - @Test - void testSingleCandidateDataSource() { - this.contextRunner - .withUserConfiguration(SingleCandidateDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(MybatisProperties.class)).hasSize(1); - }); - } - - @Test - void testDefaultConfiguration() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisLanguageDriverAutoConfiguration.class, - MybatisScanMapperConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - Map languageDriverBeans = context.getBeansOfType(LanguageDriver.class); - assertThat(languageDriverBeans).hasSize(3).containsKeys("freeMarkerLanguageDriver", "velocityLanguageDriver", - "thymeleafLanguageDriver"); - assertThat(languageDriverBeans.get("freeMarkerLanguageDriver")).isInstanceOf(FreeMarkerLanguageDriver.class); - assertThat(languageDriverBeans.get("velocityLanguageDriver")).isInstanceOf(VelocityLanguageDriver.class); - assertThat(languageDriverBeans.get("thymeleafLanguageDriver")).isInstanceOf(ThymeleafLanguageDriver.class); - LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); - assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(XMLLanguageDriver.class); - assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(XMLLanguageDriver.class); - assertThat(languageDriverRegistry.getDriver(XMLLanguageDriver.class)).isNotNull(); - assertThat(languageDriverRegistry.getDriver(RawLanguageDriver.class)).isNotNull(); - assertThat(languageDriverRegistry.getDriver(FreeMarkerLanguageDriver.class)).isNotNull(); - assertThat(languageDriverRegistry.getDriver(VelocityLanguageDriver.class)).isNotNull(); - assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); - }); - } - - @Test - void testScanWithLazy() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.lazy-initialization:true").run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(0); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - context.getBean(DateTimeMapper.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - }); - } - - @Test - void testAutoScanWithDefault() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - context.getBean(CityMapper.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") - .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) - .isEqualTo("sqlSessionTemplate"); - assertThat(context.getBeanFactory() - .getBeanDefinition(context.getBeanNamesForType(MapperScannerConfigurer.class)[0]).getRole()) - .isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE); - }); - } - - @Test - void testAutoScanWithInjectSqlSessionOnMapperScanIsFalse() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.inject-sql-session-on-mapper-scan:false").run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - context.getBean(CityMapper.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanFactory().getBeanDefinition("cityMapper").getPropertyValues() - .getPropertyValue("sqlSessionTemplate")).isNull(); - assertThat(context.getBeanFactory().getBeanDefinition("cityMapper").getPropertyValues() - .getPropertyValue("sqlSessionFactory")).isNull(); - }); - } - - @Test - void testAutoScanWithLazy() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.lazy-initialization:true").run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(0); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - context.getBean(CityMapper.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - }); - } - - @Test - void testAutoScanWithDefaultScope() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.mapper-default-scope:thread").run(context -> { - context.getBean(CityMapper.class); - BeanDefinition bd = context.getBeanFactory().getBeanDefinition("cityMapper"); - assertThat(bd.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName()); - BeanDefinition spbd = context.getBeanFactory().getBeanDefinition("scopedTarget.cityMapper"); - assertThat(spbd.getBeanClassName()).isEqualTo(MapperFactoryBean.class.getName()); - assertThat(spbd.getScope()).isEqualTo("thread"); - }); - } - - @Test - void testAutoScanWithoutDefaultScope() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .run(context -> { - context.getBean(CityMapper.class); - BeanDefinition df = context.getBeanFactory().getBeanDefinition("cityMapper"); - assertThat(df.getBeanClassName()).isEqualTo(MapperFactoryBean.class.getName()); - assertThat(df.getScope()).isEqualTo("singleton"); - }); - } - - @Test - void testWithConfigLocation() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisMapperConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config.xml").run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapperImpl.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()).isTrue(); - }); - } - - @Test - void testWithCheckConfigLocationFileExists() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisAutoConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config.xml", "mybatis.check-config-location=true") - .run(context -> assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1)); - } - - @Test - void testWithCheckConfigLocationFileNotSpecify() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) - .withPropertyValues("mybatis.check-config-location=true") - .run(context -> assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1)); - } - - @Test - void testWithCheckConfigLocationFileDoesNotExists() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) - .withPropertyValues("mybatis.config-location:foo.xml", "mybatis.check-config-location=true") - .run(context -> assertThat(context).getFailure().isInstanceOf(BeanCreationException.class).hasMessageContaining( - "Cannot find config location: class path resource [foo.xml] (please add config file or check your Mybatis configuration)")); - } - - @Test - void testWithTypeHandlersPackage() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.type-handlers-package:org.mybatis.spring.boot.autoconfigure.handler") - .run(context -> { - TypeHandlerRegistry typeHandlerRegistry = context.getBean(SqlSessionFactory.class).getConfiguration() - .getTypeHandlerRegistry(); - assertThat(typeHandlerRegistry.hasTypeHandler(BigInteger.class)).isTrue(); - assertThat(typeHandlerRegistry.hasTypeHandler(AtomicInteger.class)).isTrue(); - assertThat(typeHandlerRegistry.hasTypeHandler(AtomicLong.class)).isTrue(); - }); - } - - @Test - void testWithMapperLocation() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", - "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml") - .run( - context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getMappedStatementNames()) - .hasSize(2)); - } - - @Test - void testWithExecutorType() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisMapperConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config.xml", "mybatis.executor-type:REUSE") - .run(context -> assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()) - .isEqualTo(ExecutorType.REUSE)); - } - - @Test - void testDefaultBootConfiguration() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - }); - } - - @Test - void testWithInterceptorsOrder1() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MybatisInterceptorConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors()).hasSize(2); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(0)) - .isInstanceOf(MyInterceptor2.class); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(1)) - .isInstanceOf(MyInterceptor.class); - }); - } - - @Test - void testWithInterceptorsOrder2() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MybatisInterceptorConfiguration2.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors()).hasSize(2); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(0)) - .isInstanceOf(MyInterceptor.class); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(1)) - .isInstanceOf(MyInterceptor2.class); - }); - } - - @Test - void testWithTypeHandlers() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MybatisTypeHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getTypeHandlerRegistry() - .getTypeHandler(UUID.class)).isInstanceOf(MyTypeHandler.class); - }); - } - - @Test - void testWithDatabaseIdProvider() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, DatabaseProvidersConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .run(context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDatabaseId()) - .isEqualTo("h2")); - } - - @Test - void testMixedWithConfigurationFileAndInterceptor() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisInterceptorConfiguration.class, - CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml").run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); - assertThat(configuration.getInterceptors()).hasSize(2); - assertThat(configuration.getInterceptors().get(0)).isInstanceOf(MyInterceptor2.class); - assertThat(configuration.getInterceptors().get(1)).isInstanceOf(MyInterceptor.class); - }); - } - - @Test - void testMixedWithConfigurationFileAndDatabaseIdProvider() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - DatabaseProvidersConfiguration.class, CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml").run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); - assertThat(configuration.getDatabaseId()).isEqualTo("h2"); - }); - } - - @Test - void testMixedWithConfigurationFileAndTypeHandlersPackage() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", - "mybatis.type-handlers-package:org.mybatis.spring.boot.autoconfigure.handler.") - .run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) - .isInstanceOf(DummyTypeHandler.class); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) - .isInstanceOf(AtomicNumberTypeHandler.class); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicLong.class)) - .isInstanceOf(AtomicNumberTypeHandler.class); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) - .hasToString("type=" + AtomicInteger.class); - }); - } - - @Test - void testMixedWithConfigurationFileAndTypeAliasesPackageAndMapperLocations() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", - "mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", - "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml") - .run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); - assertThat(configuration.getMappedStatementNames()).contains("selectCityById"); - assertThat(configuration.getMappedStatementNames()) - .contains("org.mybatis.spring.boot.autoconfigure.repository.CityMapperImpl.selectCityById"); - assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("city"); - assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("name"); - }); - } - - @Test - void testMixedWithFullConfigurations() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - MybatisInterceptorConfiguration.class, DatabaseProvidersConfiguration.class, - CityMapperRepositoryConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", - "mybatis.type-handlers-package:org.mybatis.spring.**.handler", - "mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", - "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml", - "mybatis.executor-type=REUSE") - .run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) - .isInstanceOf(DummyTypeHandler.class); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) - .isInstanceOf(AtomicNumberTypeHandler.class); - assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicLong.class)) - .isInstanceOf(AtomicNumberTypeHandler.class); - assertThat(configuration.getMappedStatementNames()).hasSize(4); - assertThat(configuration.getMappedStatementNames()).contains("selectCityById"); - assertThat(configuration.getMappedStatementNames()) - .contains("org.mybatis.spring.boot.autoconfigure.repository.CityMapperImpl.selectCityById"); - assertThat(configuration.getMappedStatementNames()).contains("findById"); - assertThat(configuration.getMappedStatementNames()) - .contains("org.mybatis.spring.boot.autoconfigure.mapper.CityMapper.findById"); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.REUSE); - assertThat(configuration.getInterceptors()).hasSize(2); - assertThat(configuration.getInterceptors().get(0)).isInstanceOf(MyInterceptor2.class); - assertThat(configuration.getInterceptors().get(1)).isInstanceOf(MyInterceptor.class); - assertThat(configuration.getDatabaseId()).isEqualTo("h2"); - }); - } - - @Test - void testWithMyBatisConfiguration() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) - .withPropertyValues("mybatis.configuration.map-underscore-to-camel-case:true").run(context -> assertThat( - context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()).isTrue()); - } - - @Test - void testWithMyBatisConfigurationCustomizer() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MyBatisConfigurationCustomizerConfiguration.class) - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) - .isInstanceOf(DummyTypeHandler.class); - assertThat(sqlSessionFactory.getConfiguration().getCache("test")).isNotNull(); - }); - } - - @Test - void testWithSqlSessionFactoryBeanCustomizer() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - SqlSessionFactoryBeanCustomizerConfiguration.class).run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) - .isInstanceOf(DummyTypeHandler.class); - assertThat(sqlSessionFactory.getConfiguration().getCache("test")).isNotNull(); - }); - } - - @Test - void testConfigFileAndConfigurationWithTogether() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config.xml", - "mybatis.configuration.default-statement-timeout:30") - .run(context -> { - assertThat(context).hasFailed(); - assertThat(context).getFailure().isInstanceOf(BeanCreationException.class) - .hasMessageContaining("Property 'configuration' and 'configLocation' can not specified with together"); - }); - } - - @Test - void testWithoutConfigurationVariablesAndProperties() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .run(context -> { - Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); - assertThat(variables).isEmpty(); - }); - } - - @Test - void testWithConfigurationVariablesOnly() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.configuration.variables.key1:value1").run(context -> { - Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); - assertThat(variables).hasSize(1); - assertThat(variables.getProperty("key1")).isEqualTo("value1"); - }); - } - - @Test - void testWithConfigurationPropertiesOnly() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.configuration-properties.key2:value2").run(context -> { - Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); - assertThat(variables).hasSize(1); - assertThat(variables.getProperty("key2")).isEqualTo("value2"); - }); - } - - @Test - void testWithConfigurationVariablesAndPropertiesOtherKey() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.configuration.variables.key1:value1", - "mybatis.configuration-properties.key2:value2") - .run(context -> { - Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); - assertThat(variables).hasSize(2); - assertThat(variables.getProperty("key1")).isEqualTo("value1"); - assertThat(variables.getProperty("key2")).isEqualTo("value2"); - }); - } - - @Test - void testWithConfigurationVariablesAndPropertiesSameKey() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues("mybatis.configuration.variables.key:value1", "mybatis.configuration-properties.key:value2") - .run(context -> { - Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); - assertThat(variables).hasSize(1); - assertThat(variables.getProperty("key")).isEqualTo("value2"); - }); - } - - @Test - void testCustomSqlSessionFactory() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - CustomSqlSessionFactoryConfiguration.class, CityMapperRepositoryConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVariables().getProperty("key")) - .isEqualTo("value"); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") - .getPropertyValues().getPropertyValue("sqlSessionFactory").getValue()).getBeanName()) - .isEqualTo("customSqlSessionFactory"); - }); - } - - @Test - void testMySqlSessionFactory() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MySqlSessionFactoryConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBean(SqlSessionFactory.class)).isInstanceOf(MySqlSessionFactory.class); - }); - } - - @Test - void testCustomSqlSessionTemplate() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, - CustomSqlSessionTemplateConfiguration.class, CityMapperRepositoryConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") - .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) - .isEqualTo("customSqlSessionTemplate"); - }); - } - - @Test - void testMySqlSessionTemplate() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MySqlSessionTemplateConfiguration.class) - .run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class)).isInstanceOf(MySqlSessionTemplate.class); - }); - } - - @Test - void testCustomSqlSessionTemplateAndSqlSessionFactory() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MybatisBootMapperScanAutoConfiguration.class, CustomSqlSessionFactoryConfiguration.class, - CustomSqlSessionTemplateConfiguration.class, CityMapperRepositoryConfiguration.class).run(context -> { - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); - assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); - assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") - .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) - .isEqualTo("customSqlSessionTemplate"); - }); - } - - @Test - void testTypeAliasesSuperTypeIsSpecify() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class) - .withPropertyValues("mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", - "mybatis.type-aliases-super-type:org.mybatis.spring.boot.autoconfigure.domain.Domain") - .run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("city"); - assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).doesNotContainKey("name"); - }); - } - - @Test - void testMapperFactoryBean() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MapperFactoryBeanConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - }); - } - - @Test - void testMapperScannerConfigurer() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - MapperScannerConfigurerConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - }); - } - - @Test - void testDefaultScriptingLanguageIsSpecify() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues( - "mybatis.default-scripting-language-driver:org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver") - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); - assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(ThymeleafLanguageDriver.class); - assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(ThymeleafLanguageDriver.class); - assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); - }); - } - - @Test - void testExcludeMybatisLanguageDriverAutoConfiguration() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, - PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues( - "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); - assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); - assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); - assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); - assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) - .isFalse(); - assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(0); - }); - } - - @Test - void testMybatisLanguageDriverAutoConfigurationWithSingleCandidate() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, - SingleLanguageDriverConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues( - "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); - assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(1); - assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(ThymeleafLanguageDriver.class); - assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(ThymeleafLanguageDriver.class); - assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); - }); - } - - @Test - void testMybatisLanguageDriverAutoConfigurationWithSingleCandidateWhenDefaultLanguageDriverIsSpecify() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, - SingleLanguageDriverConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .withPropertyValues( - "mybatis.default-scripting-language-driver:org.apache.ibatis.scripting.xmltags.XMLLanguageDriver", - "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") - .run(context -> { - SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); - LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); - assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(1); - assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(XMLLanguageDriver.class); - assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(XMLLanguageDriver.class); - assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(MybatisAutoConfiguration.class)); + + @Test + void withCustomBeanNameGenerator() { + contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomBeanNameGeneratorConfiguration.class) + .withPropertyValues("mybatis.beanNameGenerator:org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator") + .run((context) -> { + assertThat(context.containsBean(com.example.mapper.two.DateTimeMapper.class.getName())).isTrue(); + assertThat(context.containsBean(com.example.mapper.one.DateTimeMapper.class.getName())).isTrue(); + }); + } + + @Configuration + @TestAutoConfigurationPackage(DateTimeMapper.class) + static class CustomBeanNameGeneratorConfiguration { + + } + + @Test + void testNoDataSource() { + this.contextRunner.withUserConfiguration(PropertyPlaceholderAutoConfiguration.class).run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).isEmpty(); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).isEmpty(); + assertThat(context.getBeanNamesForType(MybatisProperties.class)).isEmpty(); }); - } - - @Test - void whenFlywayIsAutoConfiguredThenMybatisSqlSessionTemplateDependsOnFlywayBeans() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); - contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { - BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); - assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayInitializer", "flyway"); - }); - } - - @Test - void whenCustomMigrationInitializerIsDefinedThenMybatisSqlSessionTemplateDependsOnIt() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); - contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomFlywayMigrationInitializer.class) - .run((context) -> { - BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); - assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayMigrationInitializer", "flyway"); + } + + @Test + void testMultipleDataSource() { + this.contextRunner + .withUserConfiguration(MultipleDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).isEmpty(); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).isEmpty(); + assertThat(context.getBeanNamesForType(MybatisProperties.class)).isEmpty(); + }); + } + + @Test + void testSingleCandidateDataSource() { + this.contextRunner + .withUserConfiguration(SingleCandidateDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(MybatisProperties.class)).hasSize(1); + }); + } + + @Test + void testDefaultConfiguration() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisLanguageDriverAutoConfiguration.class, + MybatisScanMapperConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + Map languageDriverBeans = context.getBeansOfType(LanguageDriver.class); + assertThat(languageDriverBeans).hasSize(3).containsKeys("freeMarkerLanguageDriver", "velocityLanguageDriver", + "thymeleafLanguageDriver"); + assertThat(languageDriverBeans.get("freeMarkerLanguageDriver")).isInstanceOf(FreeMarkerLanguageDriver.class); + assertThat(languageDriverBeans.get("velocityLanguageDriver")).isInstanceOf(VelocityLanguageDriver.class); + assertThat(languageDriverBeans.get("thymeleafLanguageDriver")).isInstanceOf(ThymeleafLanguageDriver.class); + LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); + assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(XMLLanguageDriver.class); + assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(XMLLanguageDriver.class); + assertThat(languageDriverRegistry.getDriver(XMLLanguageDriver.class)).isNotNull(); + assertThat(languageDriverRegistry.getDriver(RawLanguageDriver.class)).isNotNull(); + assertThat(languageDriverRegistry.getDriver(FreeMarkerLanguageDriver.class)).isNotNull(); + assertThat(languageDriverRegistry.getDriver(VelocityLanguageDriver.class)).isNotNull(); + assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); + }); + } + + @Test + void testScanWithLazy() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.lazy-initialization:true").run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(0); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + context.getBean(DateTimeMapper.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + }); + } + + @Test + void testAutoScanWithDefault() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + context.getBean(CityMapper.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") + .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) + .isEqualTo("sqlSessionTemplate"); + assertThat(context.getBeanFactory() + .getBeanDefinition(context.getBeanNamesForType(MapperScannerConfigurer.class)[0]).getRole()) + .isEqualTo(BeanDefinition.ROLE_INFRASTRUCTURE); + }); + } + + @Test + void testAutoScanWithInjectSqlSessionOnMapperScanIsFalse() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.inject-sql-session-on-mapper-scan:false").run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + context.getBean(CityMapper.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanFactory().getBeanDefinition("cityMapper").getPropertyValues() + .getPropertyValue("sqlSessionTemplate")).isNull(); + assertThat(context.getBeanFactory().getBeanDefinition("cityMapper").getPropertyValues() + .getPropertyValue("sqlSessionFactory")).isNull(); + }); + } + + @Test + void testAutoScanWithLazy() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.lazy-initialization:true").run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(0); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + context.getBean(CityMapper.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + }); + } + + @Test + void testAutoScanWithDefaultScope() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.mapper-default-scope:thread").run(context -> { + context.getBean(CityMapper.class); + BeanDefinition bd = context.getBeanFactory().getBeanDefinition("cityMapper"); + assertThat(bd.getBeanClassName()).isEqualTo(ScopedProxyFactoryBean.class.getName()); + BeanDefinition spbd = context.getBeanFactory().getBeanDefinition("scopedTarget.cityMapper"); + assertThat(spbd.getBeanClassName()).isEqualTo(MapperFactoryBean.class.getName()); + assertThat(spbd.getScope()).isEqualTo("thread"); + }); + } + + @Test + void testAutoScanWithoutDefaultScope() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .run(context -> { + context.getBean(CityMapper.class); + BeanDefinition df = context.getBeanFactory().getBeanDefinition("cityMapper"); + assertThat(df.getBeanClassName()).isEqualTo(MapperFactoryBean.class.getName()); + assertThat(df.getScope()).isEqualTo("singleton"); + }); + } + + @Test + void testWithConfigLocation() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisMapperConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config.xml").run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapperImpl.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()).isTrue(); + }); + } + + @Test + void testWithCheckConfigLocationFileExists() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisAutoConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config.xml", "mybatis.check-config-location=true") + .run(context -> assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1)); + } + + @Test + void testWithCheckConfigLocationFileNotSpecify() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("mybatis.check-config-location=true") + .run(context -> assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1)); + } + + @Test + void testWithCheckConfigLocationFileDoesNotExists() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("mybatis.config-location:foo.xml", "mybatis.check-config-location=true") + .run(context -> assertThat(context).getFailure().isInstanceOf(BeanCreationException.class).hasMessageContaining( + "Cannot find config location: class path resource [foo.xml] (please add config file or check your Mybatis configuration)")); + } + + @Test + void testWithTypeHandlersPackage() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.type-handlers-package:org.mybatis.spring.boot.autoconfigure.handler") + .run(context -> { + TypeHandlerRegistry typeHandlerRegistry = context.getBean(SqlSessionFactory.class).getConfiguration() + .getTypeHandlerRegistry(); + assertThat(typeHandlerRegistry.hasTypeHandler(BigInteger.class)).isTrue(); + assertThat(typeHandlerRegistry.hasTypeHandler(AtomicInteger.class)).isTrue(); + assertThat(typeHandlerRegistry.hasTypeHandler(AtomicLong.class)).isTrue(); + }); + } + + @Test + void testWithMapperLocation() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", + "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml") + .run( + context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getMappedStatementNames()) + .hasSize(2)); + } + + @Test + void testWithExecutorType() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisMapperConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config.xml", "mybatis.executor-type:REUSE") + .run(context -> assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()) + .isEqualTo(ExecutorType.REUSE)); + } + + @Test + void testDefaultBootConfiguration() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, CityMapperRepositoryConfiguration.class) + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + }); + } + + @Test + void testWithInterceptorsOrder1() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MybatisInterceptorConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors()).hasSize(2); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(0)) + .isInstanceOf(MyInterceptor2.class); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(1)) + .isInstanceOf(MyInterceptor.class); }); - } - - @Test - void whenCustomFlywayIsDefinedThenMybatisSqlSessionTemplateDependsOnIt() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); - contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomFlyway.class).run((context) -> { - BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); - assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("customFlyway"); - }); - } - - @Test - void whenLiquibaseIsAutoConfiguredThenMybatisSqlSessionTemplateDependsOnSpringLiquibaseBeans() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class, MybatisAutoConfiguration.class)); - contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { - BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); - assertThat(beanDefinition.getDependsOn()).containsExactly("liquibase"); - }); - } - - @Test - void whenCustomSpringLiquibaseIsDefinedThenMybatisSqlSessionTemplateDependsOnSpringLiquibaseBeans() { - ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class, MybatisAutoConfiguration.class)); - contextRunner.withUserConfiguration(LiquibaseUserConfiguration.class, EmbeddedDataSourceConfiguration.class) - .run((context) -> { - BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); - assertThat(beanDefinition.getDependsOn()).containsExactly("springLiquibase"); + } + + @Test + void testWithInterceptorsOrder2() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MybatisInterceptorConfiguration2.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors()).hasSize(2); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(0)) + .isInstanceOf(MyInterceptor.class); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getInterceptors().get(1)) + .isInstanceOf(MyInterceptor2.class); }); - } - - @Test - void testTypeAliasesWithMultiByteCharacterInPackageName() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class) - .withPropertyValues("mybatis.config-location:mybatis-config2.xml").run(context -> { - org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) - .getConfiguration(); - assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("シティー"); + } + + @Test + void testWithTypeHandlers() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MybatisTypeHandlerConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getTypeHandlerRegistry() + .getTypeHandler(UUID.class)).isInstanceOf(MyTypeHandler.class); }); - } + } - @Configuration - static class MultipleDataSourceConfiguration { - @Bean - DataSource dataSourcePrimary() { - return Mockito.mock(DataSource.class); + @Test + void testWithDatabaseIdProvider() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, DatabaseProvidersConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .run(context -> assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getDatabaseId()) + .isEqualTo("h2")); } - @Bean - DataSource dataSourceReplica() { - return Mockito.mock(DataSource.class); + @Test + void testMixedWithConfigurationFileAndInterceptor() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisInterceptorConfiguration.class, + CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml").run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); + assertThat(configuration.getInterceptors()).hasSize(2); + assertThat(configuration.getInterceptors().get(0)).isInstanceOf(MyInterceptor2.class); + assertThat(configuration.getInterceptors().get(1)).isInstanceOf(MyInterceptor.class); + }); } - } - @Configuration - static class SingleCandidateDataSourceConfiguration { - @Bean - @Primary - DataSource dataSourcePrimary() { - return Mockito.mock(DataSource.class); + @Test + void testMixedWithConfigurationFileAndDatabaseIdProvider() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + DatabaseProvidersConfiguration.class, CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml").run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); + assertThat(configuration.getDatabaseId()).isEqualTo("h2"); + }); } - @Bean - DataSource dataSourceReplica() { - return Mockito.mock(DataSource.class); + @Test + void testMixedWithConfigurationFileAndTypeHandlersPackage() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", + "mybatis.type-handlers-package:org.mybatis.spring.boot.autoconfigure.handler.") + .run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) + .isInstanceOf(DummyTypeHandler.class); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) + .isInstanceOf(AtomicNumberTypeHandler.class); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicLong.class)) + .isInstanceOf(AtomicNumberTypeHandler.class); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) + .hasToString("type=" + AtomicInteger.class); + }); } - } - @Configuration - @MapperScan(basePackages = "com.example.mapper", lazyInitialization = "${mybatis.lazy-initialization:false}") - static class MybatisScanMapperConfiguration { - } + @Test + void testMixedWithConfigurationFileAndTypeAliasesPackageAndMapperLocations() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", + "mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", + "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml") + .run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); + assertThat(configuration.getMappedStatementNames()).contains("selectCityById"); + assertThat(configuration.getMappedStatementNames()) + .contains("org.mybatis.spring.boot.autoconfigure.repository.CityMapperImpl.selectCityById"); + assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("city"); + assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("name"); + }); + } - @Configuration - static class MapperFactoryBeanConfiguration { - @Bean - MapperFactoryBean dateTimeMapper(SqlSessionFactory sqlSessionFactory) { - MapperFactoryBean factoryBean = new MapperFactoryBean<>(DateTimeMapper.class); - factoryBean.setSqlSessionFactory(sqlSessionFactory); - return factoryBean; + @Test + void testMixedWithFullConfigurations() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + MybatisInterceptorConfiguration.class, DatabaseProvidersConfiguration.class, + CityMapperRepositoryConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config-settings-only.xml", + "mybatis.type-handlers-package:org.mybatis.spring.**.handler", + "mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", + "mybatis.mapper-locations:classpath:org/mybatis/spring/boot/autoconfigure/repository/CityMapper.xml", + "mybatis.executor-type=REUSE") + .run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(configuration.getDefaultFetchSize()).isEqualTo(1000); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) + .isInstanceOf(DummyTypeHandler.class); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicInteger.class)) + .isInstanceOf(AtomicNumberTypeHandler.class); + assertThat(configuration.getTypeHandlerRegistry().getTypeHandler(AtomicLong.class)) + .isInstanceOf(AtomicNumberTypeHandler.class); + assertThat(configuration.getMappedStatementNames()).hasSize(4); + assertThat(configuration.getMappedStatementNames()).contains("selectCityById"); + assertThat(configuration.getMappedStatementNames()) + .contains("org.mybatis.spring.boot.autoconfigure.repository.CityMapperImpl.selectCityById"); + assertThat(configuration.getMappedStatementNames()).contains("findById"); + assertThat(configuration.getMappedStatementNames()) + .contains("org.mybatis.spring.boot.autoconfigure.mapper.CityMapper.findById"); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.REUSE); + assertThat(configuration.getInterceptors()).hasSize(2); + assertThat(configuration.getInterceptors().get(0)).isInstanceOf(MyInterceptor2.class); + assertThat(configuration.getInterceptors().get(1)).isInstanceOf(MyInterceptor.class); + assertThat(configuration.getDatabaseId()).isEqualTo("h2"); + }); } - } - @Configuration - static class MapperScannerConfigurerConfiguration { - @Bean - static MapperScannerConfigurer mapperScannerConfigurer() { - MapperScannerConfigurer configurer = new MapperScannerConfigurer(); - configurer.setBasePackage("com.example.mapper"); - return configurer; + @Test + void testWithMyBatisConfiguration() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("mybatis.configuration.map-underscore-to-camel-case:true").run(context -> assertThat( + context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()).isTrue()); } - } - @Configuration - static class MybatisBootMapperScanAutoConfiguration implements BeanFactoryPostProcessor { - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - beanFactory.registerScope("thread", new SimpleThreadScope()); + @Test + void testWithMyBatisConfigurationCustomizer() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MyBatisConfigurationCustomizerConfiguration.class) + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) + .isInstanceOf(DummyTypeHandler.class); + assertThat(sqlSessionFactory.getConfiguration().getCache("test")).isNotNull(); + }); } - } - @Configuration - static class MybatisMapperConfiguration { + @Test + void testWithSqlSessionFactoryBeanCustomizer() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + SqlSessionFactoryBeanCustomizerConfiguration.class).run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().getTypeHandler(BigInteger.class)) + .isInstanceOf(DummyTypeHandler.class); + assertThat(sqlSessionFactory.getConfiguration().getCache("test")).isNotNull(); + }); + } - @Bean - public CityMapperImpl cityMapper() { - return new CityMapperImpl(); + @Test + void testConfigFileAndConfigurationWithTogether() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config.xml", + "mybatis.configuration.default-statement-timeout:30") + .run(context -> { + assertThat(context).hasFailed(); + assertThat(context).getFailure().isInstanceOf(BeanCreationException.class) + .hasMessageContaining("Property 'configuration' and 'configLocation' can not specified with together"); + }); } - } + @Test + void testWithoutConfigurationVariablesAndProperties() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .run(context -> { + Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); + assertThat(variables).isEmpty(); + }); + } - @Configuration - static class MybatisInterceptorConfiguration { + @Test + void testWithConfigurationVariablesOnly() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.configuration.variables.key1:value1").run(context -> { + Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); + assertThat(variables).hasSize(1); + assertThat(variables.getProperty("key1")).isEqualTo("value1"); + }); + } - @Bean - @Order(2) - public MyInterceptor myInterceptor() { - return new MyInterceptor(); + @Test + void testWithConfigurationPropertiesOnly() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.configuration-properties.key2:value2").run(context -> { + Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); + assertThat(variables).hasSize(1); + assertThat(variables.getProperty("key2")).isEqualTo("value2"); + }); } - @Bean - @Order(1) - public MyInterceptor2 myInterceptor2() { - return new MyInterceptor2(); + @Test + void testWithConfigurationVariablesAndPropertiesOtherKey() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.configuration.variables.key1:value1", + "mybatis.configuration-properties.key2:value2") + .run(context -> { + Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); + assertThat(variables).hasSize(2); + assertThat(variables.getProperty("key1")).isEqualTo("value1"); + assertThat(variables.getProperty("key2")).isEqualTo("value2"); + }); } - } + @Test + void testWithConfigurationVariablesAndPropertiesSameKey() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues("mybatis.configuration.variables.key:value1", "mybatis.configuration-properties.key:value2") + .run(context -> { + Properties variables = context.getBean(SqlSessionFactory.class).getConfiguration().getVariables(); + assertThat(variables).hasSize(1); + assertThat(variables.getProperty("key")).isEqualTo("value2"); + }); + } - @Configuration - static class MybatisInterceptorConfiguration2 { + @Test + void testCustomSqlSessionFactory() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + CustomSqlSessionFactoryConfiguration.class, CityMapperRepositoryConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().getVariables().getProperty("key")) + .isEqualTo("value"); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") + .getPropertyValues().getPropertyValue("sqlSessionFactory").getValue()).getBeanName()) + .isEqualTo("customSqlSessionFactory"); + }); + } - @Bean - @Order(1) - public MyInterceptor myInterceptor() { - return new MyInterceptor(); + @Test + void testMySqlSessionFactory() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MySqlSessionFactoryConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBean(SqlSessionFactory.class)).isInstanceOf(MySqlSessionFactory.class); + }); } - @Bean - @Order(2) - public MyInterceptor2 myInterceptor2() { - return new MyInterceptor2(); + @Test + void testCustomSqlSessionTemplate() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class, + CustomSqlSessionTemplateConfiguration.class, CityMapperRepositoryConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") + .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) + .isEqualTo("customSqlSessionTemplate"); + }); } - } + @Test + void testMySqlSessionTemplate() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MySqlSessionTemplateConfiguration.class) + .run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class)).isInstanceOf(MySqlSessionTemplate.class); + }); + } - @Configuration - static class MybatisTypeHandlerConfiguration { + @Test + void testCustomSqlSessionTemplateAndSqlSessionFactory() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MybatisBootMapperScanAutoConfiguration.class, CustomSqlSessionFactoryConfiguration.class, + CustomSqlSessionTemplateConfiguration.class, CityMapperRepositoryConfiguration.class).run(context -> { + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.BATCH); + assertThat(context.getBeanNamesForType(CityMapper.class)).hasSize(1); + assertThat(((RuntimeBeanReference) context.getBeanFactory().getBeanDefinition("cityMapper") + .getPropertyValues().getPropertyValue("sqlSessionTemplate").getValue()).getBeanName()) + .isEqualTo("customSqlSessionTemplate"); + }); + } - @Bean - public MyTypeHandler myTypeHandler() { - return new MyTypeHandler(); + @Test + void testTypeAliasesSuperTypeIsSpecify() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class) + .withPropertyValues("mybatis.type-aliases-package:org.mybatis.spring.boot.autoconfigure.domain", + "mybatis.type-aliases-super-type:org.mybatis.spring.boot.autoconfigure.domain.Domain") + .run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("city"); + assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).doesNotContainKey("name"); + }); } - } + @Test + void testMapperFactoryBean() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MapperFactoryBeanConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + }); + } - @Configuration - static class MyBatisConfigurationCustomizerConfiguration { - @Bean - ConfigurationCustomizer typeHandlerConfigurationCustomizer() { - return configuration -> configuration.getTypeHandlerRegistry().register(new DummyTypeHandler()); + @Test + void testMapperScannerConfigurer() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, + MapperScannerConfigurerConfiguration.class, PropertyPlaceholderAutoConfiguration.class).run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + }); } - @Bean - ConfigurationCustomizer cacheConfigurationCustomizer() { - return configuration -> configuration.addCache(new PerpetualCache("test")); + @Test + void testDefaultScriptingLanguageIsSpecify() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues( + "mybatis.default-scripting-language-driver:org.mybatis.scripting.thymeleaf.ThymeleafLanguageDriver") + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); + assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(ThymeleafLanguageDriver.class); + assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(ThymeleafLanguageDriver.class); + assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); + }); } - } - @Configuration - static class SqlSessionFactoryBeanCustomizerConfiguration { - @Bean - SqlSessionFactoryBeanCustomizer typeHandlerSqlSessionFactoryBeanCustomizer() { - return factoryBean -> factoryBean.setTypeHandlers(new DummyTypeHandler()); + @Test + void testExcludeMybatisLanguageDriverAutoConfiguration() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, + PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues( + "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + assertThat(sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers()).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionFactory.class)).hasSize(1); + assertThat(context.getBeanNamesForType(SqlSessionTemplate.class)).hasSize(1); + assertThat(context.getBeanNamesForType(DateTimeMapper.class)).hasSize(1); + assertThat(context.getBean(SqlSessionTemplate.class).getExecutorType()).isEqualTo(ExecutorType.SIMPLE); + assertThat(context.getBean(SqlSessionFactory.class).getConfiguration().isMapUnderscoreToCamelCase()) + .isFalse(); + assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(0); + }); } - @Bean - SqlSessionFactoryBeanCustomizer cacheSqlSessionFactoryBeanCustomizer() { - return factoryBean -> factoryBean.setCache(new PerpetualCache("test")); + @Test + void testMybatisLanguageDriverAutoConfigurationWithSingleCandidate() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, + SingleLanguageDriverConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues( + "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); + assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(1); + assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(ThymeleafLanguageDriver.class); + assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(ThymeleafLanguageDriver.class); + assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); + }); } - } - @Configuration - static class SingleLanguageDriverConfiguration { - @Bean - ThymeleafLanguageDriver myThymeleafLanguageDriver() { - return new ThymeleafLanguageDriver(); + @Test + void testMybatisLanguageDriverAutoConfigurationWithSingleCandidateWhenDefaultLanguageDriverIsSpecify() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisScanMapperConfiguration.class, + SingleLanguageDriverConfiguration.class, PropertyPlaceholderAutoConfiguration.class) + .withPropertyValues( + "mybatis.default-scripting-language-driver:org.apache.ibatis.scripting.xmltags.XMLLanguageDriver", + "spring.autoconfigure.exclude:org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration") + .run(context -> { + SqlSessionFactory sqlSessionFactory = context.getBean(SqlSessionFactory.class); + LanguageDriverRegistry languageDriverRegistry = sqlSessionFactory.getConfiguration().getLanguageRegistry(); + assertThat(context.getBeanNamesForType(LanguageDriver.class)).hasSize(1); + assertThat(languageDriverRegistry.getDefaultDriverClass()).isEqualTo(XMLLanguageDriver.class); + assertThat(languageDriverRegistry.getDefaultDriver()).isInstanceOf(XMLLanguageDriver.class); + assertThat(languageDriverRegistry.getDriver(ThymeleafLanguageDriver.class)).isNotNull(); + }); } - } - @Intercepts(@Signature(type = Map.class, method = "get", args = { Object.class })) - static class MyInterceptor implements Interceptor { + @Test + void whenFlywayIsAutoConfiguredThenMybatisSqlSessionTemplateDependsOnFlywayBeans() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); + contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { + BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); + assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayInitializer", "flyway"); + }); + } - @Override - public Object intercept(Invocation invocation) { - return "Test"; + @Test + void whenCustomMigrationInitializerIsDefinedThenMybatisSqlSessionTemplateDependsOnIt() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); + contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomFlywayMigrationInitializer.class) + .run((context) -> { + BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); + assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("flywayMigrationInitializer", "flyway"); + }); } - @Override - public Object plugin(Object target) { - return Plugin.wrap(target, this); + @Test + void whenCustomFlywayIsDefinedThenMybatisSqlSessionTemplateDependsOnIt() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class, MybatisAutoConfiguration.class)); + contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, CustomFlyway.class).run((context) -> { + BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); + assertThat(beanDefinition.getDependsOn()).containsExactlyInAnyOrder("customFlyway"); + }); } - @Override - public void setProperties(Properties properties) { + @Test + void whenLiquibaseIsAutoConfiguredThenMybatisSqlSessionTemplateDependsOnSpringLiquibaseBeans() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class, MybatisAutoConfiguration.class)); + contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { + BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); + assertThat(beanDefinition.getDependsOn()).containsExactly("liquibase"); + }); + } + @Test + void whenCustomSpringLiquibaseIsDefinedThenMybatisSqlSessionTemplateDependsOnSpringLiquibaseBeans() { + ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class, MybatisAutoConfiguration.class)); + contextRunner.withUserConfiguration(LiquibaseUserConfiguration.class, EmbeddedDataSourceConfiguration.class) + .run((context) -> { + BeanDefinition beanDefinition = context.getBeanFactory().getBeanDefinition("sqlSessionTemplate"); + assertThat(beanDefinition.getDependsOn()).containsExactly("springLiquibase"); + }); } - } - @Intercepts(@Signature(type = Map.class, method = "get", args = { Object.class })) - static class MyInterceptor2 implements Interceptor { + @Test + void testTypeAliasesWithMultiByteCharacterInPackageName() { + this.contextRunner + .withUserConfiguration(EmbeddedDataSourceConfiguration.class, MybatisBootMapperScanAutoConfiguration.class) + .withPropertyValues("mybatis.config-location:mybatis-config2.xml").run(context -> { + org.apache.ibatis.session.Configuration configuration = context.getBean(SqlSessionFactory.class) + .getConfiguration(); + assertThat(configuration.getTypeAliasRegistry().getTypeAliases()).containsKey("シティー"); + }); + } - @Override - public Object intercept(Invocation invocation) { - return "Test2"; + @Configuration + static class MultipleDataSourceConfiguration { + @Bean + DataSource dataSourcePrimary() { + return Mockito.mock(DataSource.class); + } + + @Bean + DataSource dataSourceReplica() { + return Mockito.mock(DataSource.class); + } } - @Override - public Object plugin(Object target) { - return Plugin.wrap(target, this); + @Configuration + static class SingleCandidateDataSourceConfiguration { + @Bean + @Primary + DataSource dataSourcePrimary() { + return Mockito.mock(DataSource.class); + } + + @Bean + DataSource dataSourceReplica() { + return Mockito.mock(DataSource.class); + } } - @Override - public void setProperties(Properties properties) { + @Configuration + @MapperScan(basePackages = "com.example.mapper", lazyInitialization = "${mybatis.lazy-initialization:false}") + static class MybatisScanMapperConfiguration { + } + @Configuration + static class MapperFactoryBeanConfiguration { + @Bean + MapperFactoryBean dateTimeMapper(SqlSessionFactory sqlSessionFactory) { + MapperFactoryBean factoryBean = new MapperFactoryBean<>(DateTimeMapper.class); + factoryBean.setSqlSessionFactory(sqlSessionFactory); + return factoryBean; + } } - } - @Configuration - static class DatabaseProvidersConfiguration { + @Configuration + static class MapperScannerConfigurerConfiguration { + @Bean + static MapperScannerConfigurer mapperScannerConfigurer() { + MapperScannerConfigurer configurer = new MapperScannerConfigurer(); + configurer.setBasePackage("com.example.mapper"); + return configurer; + } + } + + @Configuration + static class MybatisBootMapperScanAutoConfiguration implements BeanFactoryPostProcessor { + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.registerScope("thread", new SimpleThreadScope()); + } + } - @Bean - public PropertiesFactoryBean vendorProperties() { - Properties properties = new Properties(); - properties.put("SQL Server", "sqlserver"); - properties.put("DB2", "db2"); - properties.put("H2", "h2"); + @Configuration + static class MybatisMapperConfiguration { + + @Bean + public CityMapperImpl cityMapper() { + return new CityMapperImpl(); + } - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setProperties(properties); - return propertiesFactoryBean; } - @Bean - public VendorDatabaseIdProvider vendorDatabaseIdProvider( - @Qualifier("vendorProperties") Properties vendorProperties) { - VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(); - databaseIdProvider.setProperties(vendorProperties); - return databaseIdProvider; + @Configuration + static class MybatisInterceptorConfiguration { + + @Bean + @Order(2) + public MyInterceptor myInterceptor() { + return new MyInterceptor(); + } + + @Bean + @Order(1) + public MyInterceptor2 myInterceptor2() { + return new MyInterceptor2(); + } + } - } + @Configuration + static class MybatisInterceptorConfiguration2 { + + @Bean + @Order(1) + public MyInterceptor myInterceptor() { + return new MyInterceptor(); + } + + @Bean + @Order(2) + public MyInterceptor2 myInterceptor2() { + return new MyInterceptor2(); + } - @Configuration - static class CustomSqlSessionFactoryConfiguration { - @Bean - public SqlSessionFactory customSqlSessionFactory(DataSource dataSource) throws Exception { - SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); - sqlSessionFactoryBean.setDataSource(dataSource); - Properties props = new Properties(); - props.setProperty("key", "value"); - sqlSessionFactoryBean.setConfigurationProperties(props); - return sqlSessionFactoryBean.getObject(); } - } - @Configuration - static class MySqlSessionFactoryConfiguration { - @Bean - public SqlSessionFactory sqlSessionFactory(DataSource dataSource) { - MySqlSessionFactory sqlSessionFactory = new MySqlSessionFactory(new org.apache.ibatis.session.Configuration()); - sqlSessionFactory.getConfiguration() - .setEnvironment(new Environment("", new SpringManagedTransactionFactory(), dataSource)); - return sqlSessionFactory; + @Configuration + static class MybatisTypeHandlerConfiguration { + + @Bean + public MyTypeHandler myTypeHandler() { + return new MyTypeHandler(); + } + + } + + @Configuration + static class MyBatisConfigurationCustomizerConfiguration { + @Bean + ConfigurationCustomizer typeHandlerConfigurationCustomizer() { + return configuration -> configuration.getTypeHandlerRegistry().register(new DummyTypeHandler()); + } + + @Bean + ConfigurationCustomizer cacheConfigurationCustomizer() { + return configuration -> configuration.addCache(new PerpetualCache("test")); + } } - } - @Configuration - static class CustomSqlSessionTemplateConfiguration { - @Bean - public SqlSessionTemplate customSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { - return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH); + @Configuration + static class SqlSessionFactoryBeanCustomizerConfiguration { + @Bean + SqlSessionFactoryBeanCustomizer typeHandlerSqlSessionFactoryBeanCustomizer() { + return factoryBean -> factoryBean.setTypeHandlers(new DummyTypeHandler()); + } + + @Bean + SqlSessionFactoryBeanCustomizer cacheSqlSessionFactoryBeanCustomizer() { + return factoryBean -> factoryBean.setCache(new PerpetualCache("test")); + } } - } - @Configuration - static class MySqlSessionTemplateConfiguration { - @Bean - public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { - return new MySqlSessionTemplate(sqlSessionFactory); + @Configuration + static class SingleLanguageDriverConfiguration { + @Bean + ThymeleafLanguageDriver myThymeleafLanguageDriver() { + return new ThymeleafLanguageDriver(); + } + } + + @Intercepts(@Signature(type = Map.class, method = "get", args = {Object.class})) + static class MyInterceptor implements Interceptor { + + @Override + public Object intercept(Invocation invocation) { + return "Test"; + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } } - } - static class MySqlSessionFactory extends DefaultSqlSessionFactory { - MySqlSessionFactory(org.apache.ibatis.session.Configuration configuration) { - super(configuration); + @Intercepts(@Signature(type = Map.class, method = "get", args = {Object.class})) + static class MyInterceptor2 implements Interceptor { + + @Override + public Object intercept(Invocation invocation) { + return "Test2"; + } + + @Override + public Object plugin(Object target) { + return Plugin.wrap(target, this); + } + + @Override + public void setProperties(Properties properties) { + + } } - } - static class MySqlSessionTemplate extends SqlSessionTemplate { - MySqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { - super(sqlSessionFactory); + @Configuration + static class DatabaseProvidersConfiguration { + + @Bean + public PropertiesFactoryBean vendorProperties() { + Properties properties = new Properties(); + properties.put("SQL Server", "sqlserver"); + properties.put("DB2", "db2"); + properties.put("H2", "h2"); + + PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); + propertiesFactoryBean.setProperties(properties); + return propertiesFactoryBean; + } + + @Bean + public VendorDatabaseIdProvider vendorDatabaseIdProvider( + @Qualifier("vendorProperties") Properties vendorProperties) { + VendorDatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider(); + databaseIdProvider.setProperties(vendorProperties); + return databaseIdProvider; + } + } - } - static class MyTypeHandler extends BaseTypeHandler { + @Configuration + static class CustomSqlSessionFactoryConfiguration { + @Bean + public SqlSessionFactory customSqlSessionFactory(DataSource dataSource) throws Exception { + SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); + sqlSessionFactoryBean.setDataSource(dataSource); + Properties props = new Properties(); + props.setProperty("key", "value"); + sqlSessionFactoryBean.setConfigurationProperties(props); + return sqlSessionFactoryBean.getObject(); + } + } - @Override - public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) { + @Configuration + static class MySqlSessionFactoryConfiguration { + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) { + MySqlSessionFactory sqlSessionFactory = new MySqlSessionFactory(new org.apache.ibatis.session.Configuration()); + sqlSessionFactory.getConfiguration() + .setEnvironment(new Environment("", new SpringManagedTransactionFactory(), dataSource)); + return sqlSessionFactory; + } + } + @Configuration + static class CustomSqlSessionTemplateConfiguration { + @Bean + public SqlSessionTemplate customSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH); + } } - @Override - public UUID getNullableResult(ResultSet rs, String columnName) { - return null; + @Configuration + static class MySqlSessionTemplateConfiguration { + @Bean + public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + return new MySqlSessionTemplate(sqlSessionFactory); + } } - @Override - public UUID getNullableResult(ResultSet rs, int columnIndex) { - return null; + static class MySqlSessionFactory extends DefaultSqlSessionFactory { + MySqlSessionFactory(org.apache.ibatis.session.Configuration configuration) { + super(configuration); + } } - @Override - public UUID getNullableResult(CallableStatement cs, int columnIndex) { - return null; + static class MySqlSessionTemplate extends SqlSessionTemplate { + MySqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { + super(sqlSessionFactory); + } } - } + static class MyTypeHandler extends BaseTypeHandler { - @Configuration - @TestAutoConfigurationPackage(CityMapper.class) - static class CityMapperRepositoryConfiguration { + @Override + public void setNonNullParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) { - } + } - @Configuration(proxyBeanMethods = false) - static class CustomFlywayMigrationInitializer { + @Override + public UUID getNullableResult(ResultSet rs, String columnName) { + return null; + } + + @Override + public UUID getNullableResult(ResultSet rs, int columnIndex) { + return null; + } + + @Override + public UUID getNullableResult(CallableStatement cs, int columnIndex) { + return null; + } + + } + + @Configuration + @TestAutoConfigurationPackage(CityMapper.class) + static class CityMapperRepositoryConfiguration { - @Bean - FlywayMigrationInitializer flywayMigrationInitializer(Flyway flyway) { - FlywayMigrationInitializer initializer = new FlywayMigrationInitializer(flyway); - initializer.setOrder(Ordered.HIGHEST_PRECEDENCE); - return initializer; } - } + @Configuration(proxyBeanMethods = false) + static class CustomFlywayMigrationInitializer { - @Configuration(proxyBeanMethods = false) - static class CustomFlyway { + @Bean + FlywayMigrationInitializer flywayMigrationInitializer(Flyway flyway) { + FlywayMigrationInitializer initializer = new FlywayMigrationInitializer(flyway); + initializer.setOrder(Ordered.HIGHEST_PRECEDENCE); + return initializer; + } - @Bean - Flyway customFlyway() { - return Flyway.configure().load(); } - } + @Configuration(proxyBeanMethods = false) + static class CustomFlyway { - @Configuration(proxyBeanMethods = false) - static class LiquibaseUserConfiguration { + @Bean + Flyway customFlyway() { + return Flyway.configure().load(); + } - @Bean - SpringLiquibase springLiquibase(DataSource dataSource) { - SpringLiquibase liquibase = new SpringLiquibase(); - liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml"); - liquibase.setShouldRun(true); - liquibase.setDataSource(dataSource); - return liquibase; } - } + @Configuration(proxyBeanMethods = false) + static class LiquibaseUserConfiguration { + + @Bean + SpringLiquibase springLiquibase(DataSource dataSource) { + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.yaml"); + liquibase.setShouldRun(true); + liquibase.setDataSource(dataSource); + return liquibase; + } + + } }