diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration.java index e4b5aad8a3..b17bd3bf40 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.data.jdbc; +import java.util.Optional; import java.util.Set; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -28,11 +29,22 @@ import org.springframework.boot.autoconfigure.domain.EntityScanner; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.jdbc.core.JdbcAggregateTemplate; +import org.springframework.data.jdbc.core.convert.DataAccessStrategy; +import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; +import org.springframework.data.jdbc.core.convert.RelationResolver; +import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; import org.springframework.data.jdbc.repository.config.JdbcRepositoryConfigExtension; +import org.springframework.data.relational.RelationalManagedTypes; +import org.springframework.data.relational.core.dialect.Dialect; +import org.springframework.data.relational.core.mapping.NamingStrategy; import org.springframework.data.relational.core.mapping.Table; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.transaction.PlatformTransactionManager; @@ -79,6 +91,59 @@ public class JdbcRepositoriesAutoConfiguration { return new EntityScanner(this.applicationContext).scan(Table.class); } + @Override + @Bean + @ConditionalOnMissingBean + public RelationalManagedTypes jdbcManagedTypes() throws ClassNotFoundException { + return super.jdbcManagedTypes(); + } + + @Override + @Bean + @ConditionalOnMissingBean + public JdbcMappingContext jdbcMappingContext(Optional namingStrategy, + JdbcCustomConversions customConversions, RelationalManagedTypes jdbcManagedTypes) { + return super.jdbcMappingContext(namingStrategy, customConversions, jdbcManagedTypes); + } + + @Override + @Bean + @ConditionalOnMissingBean + public JdbcConverter jdbcConverter(JdbcMappingContext mappingContext, NamedParameterJdbcOperations operations, + @Lazy RelationResolver relationResolver, JdbcCustomConversions conversions, Dialect dialect) { + return super.jdbcConverter(mappingContext, operations, relationResolver, conversions, dialect); + } + + @Override + @Bean + @ConditionalOnMissingBean + public JdbcCustomConversions jdbcCustomConversions() { + return super.jdbcCustomConversions(); + } + + @Override + @Bean + @ConditionalOnMissingBean + public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicationContext, + JdbcMappingContext mappingContext, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) { + return super.jdbcAggregateTemplate(applicationContext, mappingContext, converter, dataAccessStrategy); + } + + @Override + @Bean + @ConditionalOnMissingBean + public DataAccessStrategy dataAccessStrategyBean(NamedParameterJdbcOperations operations, + JdbcConverter jdbcConverter, JdbcMappingContext context, Dialect dialect) { + return super.dataAccessStrategyBean(operations, jdbcConverter, context, dialect); + } + + @Override + @Bean + @ConditionalOnMissingBean + public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { + return super.jdbcDialect(operations); + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfigurationTests.java index e0ccf5543f..45ea35ea7b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/jdbc/JdbcRepositoriesAutoConfigurationTests.java @@ -21,6 +21,7 @@ import java.util.function.Function; import javax.sql.DataSource; import org.junit.jupiter.api.Test; +import org.mockito.Answers; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; @@ -32,16 +33,24 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerA import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration; import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.ManagedTypes; +import org.springframework.data.jdbc.core.JdbcAggregateTemplate; +import org.springframework.data.jdbc.core.convert.DataAccessStrategy; +import org.springframework.data.jdbc.core.convert.JdbcConverter; +import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration; import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.relational.RelationalManagedTypes; +import org.springframework.data.relational.core.dialect.Dialect; import org.springframework.data.repository.Repository; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link JdbcRepositoriesAutoConfiguration}. @@ -128,6 +137,56 @@ class JdbcRepositoriesAutoConfigurationTests { }); } + @Test + void allowsUserToDefineCustomRelationalManagedTypes() { + allowsUserToDefineCustomBean(RelationalManagedTypesConfiguration.class, RelationalManagedTypes.class, + "customRelationalManagedTypes"); + } + + @Test + void allowsUserToDefineCustomJdbcMappingContext() { + allowsUserToDefineCustomBean(JdbcMappingContextConfiguration.class, JdbcMappingContext.class, + "customJdbcMappingContext"); + } + + @Test + void allowsUserToDefineCustomJdbcConverter() { + allowsUserToDefineCustomBean(JdbcConverterConfiguration.class, JdbcConverter.class, "customJdbcConverter"); + } + + @Test + void allowsUserToDefineCustomJdbcCustomConversions() { + allowsUserToDefineCustomBean(JdbcCustomConversionsConfiguration.class, JdbcCustomConversions.class, + "customJdbcCustomConversions"); + } + + @Test + void allowsUserToDefineCustomJdbcAggregateTemplate() { + allowsUserToDefineCustomBean(JdbcAggregateTemplateConfiguration.class, JdbcAggregateTemplate.class, + "customJdbcAggregateTemplate"); + } + + @Test + void allowsUserToDefineCustomDataAccessStrategy() { + allowsUserToDefineCustomBean(DataAccessStrategyConfiguration.class, DataAccessStrategy.class, + "customDataAccessStrategy"); + } + + @Test + void allowsUserToDefineCustomDialect() { + allowsUserToDefineCustomBean(DialectConfiguration.class, Dialect.class, "customDialect"); + } + + private void allowsUserToDefineCustomBean(Class configuration, Class beanType, String beanName) { + this.contextRunner.with(database()) + .withConfiguration(AutoConfigurations.of(JdbcTemplateAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class)) + .withUserConfiguration(configuration, EmptyConfiguration.class).run((context) -> { + assertThat(context).hasSingleBean(beanType); + assertThat(context).hasBean(beanName); + }); + } + private Function database() { return (runner) -> runner .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, @@ -154,4 +213,74 @@ class JdbcRepositoriesAutoConfigurationTests { } + @Configuration(proxyBeanMethods = false) + static class RelationalManagedTypesConfiguration { + + @Bean + RelationalManagedTypes customRelationalManagedTypes() { + return RelationalManagedTypes.empty(); + } + + } + + @Configuration(proxyBeanMethods = false) + static class JdbcMappingContextConfiguration { + + @Bean + JdbcMappingContext customJdbcMappingContext() { + return mock(JdbcMappingContext.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class JdbcConverterConfiguration { + + @Bean + JdbcConverter customJdbcConverter() { + return mock(JdbcConverter.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class JdbcCustomConversionsConfiguration { + + @Bean + JdbcCustomConversions customJdbcCustomConversions() { + return mock(JdbcCustomConversions.class, Answers.RETURNS_MOCKS); + } + + } + + @Configuration(proxyBeanMethods = false) + static class JdbcAggregateTemplateConfiguration { + + @Bean + JdbcAggregateTemplate customJdbcAggregateTemplate() { + return mock(JdbcAggregateTemplate.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class DataAccessStrategyConfiguration { + + @Bean + DataAccessStrategy customDataAccessStrategy() { + return mock(DataAccessStrategy.class); + } + + } + + @Configuration(proxyBeanMethods = false) + static class DialectConfiguration { + + @Bean + Dialect customDialect() { + return mock(Dialect.class, Answers.RETURNS_MOCKS); + } + + } + }