From 90b4ced7a6a9bdd8e61485247fca0283e5eb863d Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 23 Mar 2021 16:50:22 +0000 Subject: [PATCH] Move DataSource init auto-config out of DataSourceAutoConfiguration Previously, the auto-configuration for DataSource initialization and the properties used to configure it were part of the general DataSource auto-configuration and properties. This commit moves the auto-configuration of DataSource initialization out into a separate top-level auto-configuration class. Similarly, the properties for configuring DataSource initialization have been moved from `spring.datasource.*` into `spring.sql.init.*`. The old initialization-related `spring.datasource.*` properties have been deprecated but can still be used. When they are used, they new, separate initialization auto-configuration will back off. In other words, the initialization related `spring.datasource.*` properties and the `spring.sql.init.*` properties cannot be used in combination. Closes gh-25323 --- .../DocumentConfigurationProperties.java | 2 +- ...althContributorAutoConfigurationTests.java | 5 +- ...urcePoolMetricsAutoConfigurationTests.java | 17 ++- ...ibernateMetricsAutoConfigurationTests.java | 9 +- .../liquibase/LiquibaseEndpointTests.java | 27 +++- .../jdbc/DataSourceAutoConfiguration.java | 1 + ...DataSourceInitializationConfiguration.java | 47 +++++- .../jdbc/DataSourceProperties.java | 46 +++++- .../SqlInitializationAutoConfiguration.java | 93 ++++++++++++ .../sql/init/SqlInitializationProperties.java | 141 ++++++++++++++++++ .../autoconfigure/sql/init/package-info.java | 20 +++ ...itional-spring-configuration-metadata.json | 6 + .../main/resources/META-INF/spring.factories | 3 +- .../JobLauncherApplicationRunnerTests.java | 3 +- ...iveRepositoriesAutoConfigurationTests.java | 6 +- ...dbcRepositoriesAutoConfigurationTests.java | 12 +- ...ngoRepositoriesAutoConfigurationTests.java | 10 +- ...ingRepositoriesAutoConfigurationTests.java | 4 +- ...o4jRepositoriesAutoConfigurationTests.java | 4 +- ...stJpaDependencyAutoConfigurationTests.java | 5 +- .../DataSourceAutoConfigurationTests.java | 17 ++- ...aSourceInitializationIntegrationTests.java | 1 + .../jdbc/DataSourceJmxConfigurationTests.java | 28 +++- .../jdbc/DataSourcePropertiesTests.java | 4 +- .../HikariDataSourceConfigurationTests.java | 5 +- ...iverConfigurationFailureAnalyzerTests.java | 8 +- .../JdbcTemplateAutoConfigurationTests.java | 19 ++- ...OracleUcpDataSourceConfigurationTests.java | 5 +- .../AbstractJpaAutoConfigurationTests.java | 6 +- ...ibernate2ndLevelCacheIntegrationTests.java | 3 +- .../HibernateJpaAutoConfigurationTests.java | 69 ++++++--- .../SecurityAutoConfigurationTests.java | 6 +- .../src/docs/asciidoc/howto.adoc | 14 +- 33 files changed, 533 insertions(+), 113 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/package-info.java diff --git a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java index cbb23b79e5..86ae8f05b0 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/context/properties/DocumentConfigurationProperties.java @@ -78,7 +78,7 @@ public class DocumentConfigurationProperties extends DefaultTask { .withKeyPrefixes("spring.freemarker", "spring.groovy", "spring.mustache", "spring.thymeleaf") .addOverride("spring.groovy.template.configuration", "See GroovyMarkupConfigurer") .addSection("security").withKeyPrefixes("spring.security").addSection("data-migration") - .withKeyPrefixes("spring.flyway", "spring.liquibase").addSection("data") + .withKeyPrefixes("spring.flyway", "spring.liquibase", "spring.sql.init").addSection("data") .withKeyPrefixes("spring.couchbase", "spring.elasticsearch", "spring.h2", "spring.influx", "spring.ldap", "spring.mongodb", "spring.neo4j", "spring.redis", "spring.dao", "spring.data", "spring.datasource", "spring.jooq", "spring.jdbc", "spring.jpa", "spring.r2dbc") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java index 31b181288c..1950cec915 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -50,8 +50,7 @@ class DataSourceHealthContributorAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, - HealthContributorAutoConfiguration.class, DataSourceHealthContributorAutoConfiguration.class)) - .withPropertyValues("spring.datasource.initialization-mode=never"); + HealthContributorAutoConfiguration.class, DataSourceHealthContributorAutoConfiguration.class)); @Test void runShouldCreateIndicator() { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java index 0b1453e1c9..9453865331 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -33,6 +33,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.actuate.autoconfigure.metrics.test.MetricsRun; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -101,7 +102,8 @@ class DataSourcePoolMetricsAutoConfigurationTests { } @Test - void autoConfiguredHikariDataSourceIsInstrumentedWhenUsingDataSourceInitialization() { + @Deprecated + void autoConfiguredHikariDataSourceIsInstrumentedWhenUsingDeprecatedDataSourceInitialization() { this.contextRunner.withPropertyValues("spring.datasource.schema:db/create-custom-schema.sql") .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)).run((context) -> { context.getBean(DataSource.class).getConnection(); @@ -110,6 +112,17 @@ class DataSourcePoolMetricsAutoConfigurationTests { }); } + @Test + void autoConfiguredHikariDataSourceIsInstrumentedWhenUsingDataSourceInitialization() { + this.contextRunner.withPropertyValues("spring.sql.init.schema:db/create-custom-schema.sql").withConfiguration( + AutoConfigurations.of(DataSourceAutoConfiguration.class, SqlInitializationAutoConfiguration.class)) + .run((context) -> { + context.getBean(DataSource.class).getConnection(); + MeterRegistry registry = context.getBean(MeterRegistry.class); + registry.get("hikaricp.connections").meter(); + }); + } + @Test void hikariCanBeInstrumentedAfterThePoolHasBeenSealed() { this.contextRunner.withUserConfiguration(HikariSealingConfiguration.class) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/orm/jpa/HibernateMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/orm/jpa/HibernateMetricsAutoConfigurationTests.java index 5c4c2962fb..2f372877b9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/orm/jpa/HibernateMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/orm/jpa/HibernateMetricsAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -37,6 +37,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -132,9 +133,9 @@ class HibernateMetricsAutoConfigurationTests { @Test void entityManagerFactoryInstrumentationDoesNotDeadlockWithDeferredInitialization() { - this.contextRunner - .withPropertyValues("spring.jpa.properties.hibernate.generate_statistics:true", - "spring.datasource.schema=city-schema.sql", "spring.datasource.data=city-data.sql") + this.contextRunner.withPropertyValues("spring.jpa.properties.hibernate.generate_statistics:true", + "spring.sql.init.schema-locations:city-schema.sql", "spring.sql.init.data-locations=city-data.sql") + .withConfiguration(AutoConfigurations.of(SqlInitializationAutoConfiguration.class)) .withBean(EntityManagerFactoryBuilderCustomizer.class, () -> (builder) -> builder.setBootstrapExecutor(new SimpleAsyncTaskExecutor())) .run((context) -> { diff --git a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java index 9ad82aae63..800cdb1af9 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java +++ b/spring-boot-project/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/liquibase/LiquibaseEndpointTests.java @@ -18,7 +18,9 @@ package org.springframework.boot.actuate.liquibase; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; import java.util.Map; +import java.util.UUID; import javax.sql.DataSource; @@ -30,6 +32,8 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; +import org.springframework.boot.jdbc.init.DataSourceInitializationSettings; +import org.springframework.boot.jdbc.init.ScriptDataSourceInitializer; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -75,10 +79,8 @@ class LiquibaseEndpointTests { @Test void invokeWithCustomSchema() { - this.contextRunner.withUserConfiguration(Config.class) - .withPropertyValues("spring.liquibase.default-schema=CUSTOMSCHEMA", - "spring.datasource.schema=classpath:/db/create-custom-schema.sql") - .run((context) -> { + this.contextRunner.withUserConfiguration(Config.class, DataSourceWithSchemaConfiguration.class) + .withPropertyValues("spring.liquibase.default-schema=CUSTOMSCHEMA").run((context) -> { Map liquibaseBeans = context.getBean(LiquibaseEndpoint.class) .liquibaseBeans().getContexts().get(context.getId()).getLiquibaseBeans(); assertThat(liquibaseBeans.get("liquibase").getChangeSets()).hasSize(1); @@ -138,6 +140,23 @@ class LiquibaseEndpointTests { } + @Configuration(proxyBeanMethods = false) + static class DataSourceWithSchemaConfiguration { + + @Bean + DataSource dataSource() { + DataSource dataSource = new EmbeddedDatabaseBuilder() + .setType(EmbeddedDatabaseConnection.get(getClass().getClassLoader()).getType()) + .setName(UUID.randomUUID().toString()).build(); + DataSourceInitializationSettings settings = new DataSourceInitializationSettings(); + settings.setSchemaLocations(Arrays.asList("classpath:/db/create-custom-schema.sql")); + ScriptDataSourceInitializer initializer = new ScriptDataSourceInitializer(dataSource, settings); + initializer.initializeDatabase(); + return dataSource; + } + + } + @Configuration(proxyBeanMethods = false) static class MultipleDataSourceLiquibaseConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 5b784ec7e2..6dbdece9c2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -50,6 +50,7 @@ import org.springframework.util.StringUtils; * @author Kazuki Shimizu * @since 1.0.0 */ +@SuppressWarnings("deprecation") @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationConfiguration.java index 9a4ee42c63..e4609776ca 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationConfiguration.java @@ -18,17 +18,26 @@ package org.springframework.boot.autoconfigure.jdbc; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.sql.DataSource; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; +import org.springframework.boot.autoconfigure.condition.ConditionMessage; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.DifferentCredentialsCondition; +import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.DataSourceInitializationCondition; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; @@ -36,11 +45,12 @@ import org.springframework.boot.jdbc.init.DataSourceInitializationSettings; import org.springframework.boot.jdbc.init.ScriptDataSourceInitializer; import org.springframework.boot.jdbc.init.dependency.DataSourceInitializationDependencyConfigurer; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.DependsOn; -import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import org.springframework.util.StringUtils; @@ -50,6 +60,7 @@ import org.springframework.util.StringUtils; * * @author Andy Wilkinson */ +@Deprecated class DataSourceInitializationConfiguration { private static DataSource determineDataSource(Supplier dataSource, String username, String password, @@ -128,10 +139,12 @@ class DataSourceInitializationConfiguration { } - @Configuration(proxyBeanMethods = false) + // Fully-qualified to work around javac bug in JDK 1.8 + @org.springframework.context.annotation.Configuration(proxyBeanMethods = false) + @org.springframework.context.annotation.Import(DataSourceInitializationDependencyConfigurer.class) + @org.springframework.context.annotation.Conditional(DataSourceInitializationCondition.class) @ConditionalOnSingleCandidate(DataSource.class) @ConditionalOnMissingBean(ScriptDataSourceInitializer.class) - @Import(DataSourceInitializationDependencyConfigurer.class) static class SharedCredentialsDataSourceInitializationConfiguration { @Bean @@ -147,6 +160,32 @@ class DataSourceInitializationConfiguration { properties.getInitializationMode()); } + static class DataSourceInitializationCondition extends SpringBootCondition { + + private static final Set INITIALIZATION_PROPERTIES = Collections + .unmodifiableSet(new HashSet<>(Arrays.asList("spring.datasource.initialization-mode", + "spring.datasource.platform", "spring.datasource.schema", "spring.datasource.schema[0]", + "spring.datasource.schema-username", "spring.datasource.schema-password", + "spring.datasource.data", "spring.datasource.data[0]", "spring.datasource.data-username", + "spring.datasource.data-password", "spring.datasource.continue-on-error", + "spring.datasource.separator", "spring.datasource.sql-script-encoding"))); + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ConditionMessage.Builder message = ConditionMessage.forCondition("DataSource Initialization"); + Environment environment = context.getEnvironment(); + Set configuredProperties = INITIALIZATION_PROPERTIES.stream() + .filter(environment::containsProperty).collect(Collectors.toSet()); + if (configuredProperties.isEmpty()) { + return ConditionOutcome + .noMatch(message.didNotFind("configured properties").items(INITIALIZATION_PROPERTIES)); + } + return ConditionOutcome.match( + message.found("configured property", "configured properties").items(configuredProperties)); + } + + } + } static class InitializationModeDataSourceScriptDatabaseInitializer extends ScriptDataSourceInitializer { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index 31b24f0a12..3c86b3830b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -28,6 +28,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceInitializationMode; import org.springframework.boot.jdbc.DatabaseDriver; @@ -98,12 +99,14 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB * Mode to apply when determining if DataSource initialization should be performed * using the available DDL and DML scripts. */ + @Deprecated private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED; /** * Platform to use in the DDL or DML scripts (such as schema-${platform}.sql or * data-${platform}.sql). */ + @Deprecated private String platform = "all"; /** @@ -114,41 +117,49 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB /** * Username of the database to execute DDL scripts (if different). */ + @Deprecated private String schemaUsername; /** * Password of the database to execute DDL scripts (if different). */ + @Deprecated private String schemaPassword; /** * Data (DML) script resource references. */ + @Deprecated private List data; /** * Username of the database to execute DML scripts (if different). */ + @Deprecated private String dataUsername; /** * Password of the database to execute DML scripts (if different). */ + @Deprecated private String dataPassword; /** * Whether to stop if an error occurs while initializing the database. */ + @Deprecated private boolean continueOnError = false; /** * Statement separator in SQL initialization scripts. */ + @Deprecated private String separator = ";"; /** * SQL scripts encoding. */ + @Deprecated private Charset sqlScriptEncoding; private EmbeddedDatabaseConnection embeddedDatabaseConnection = EmbeddedDatabaseConnection.NONE; @@ -374,90 +385,123 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB this.jndiName = jndiName; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.enabled") public DataSourceInitializationMode getInitializationMode() { return this.initializationMode; } + @Deprecated public void setInitializationMode(DataSourceInitializationMode initializationMode) { this.initializationMode = initializationMode; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.platform") public String getPlatform() { return this.platform; } + @Deprecated public void setPlatform(String platform) { this.platform = platform; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.ddlScriptLocations") public List getSchema() { return this.schema; } + @Deprecated public void setSchema(List schema) { this.schema = schema; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.username") public String getSchemaUsername() { return this.schemaUsername; } + @Deprecated public void setSchemaUsername(String schemaUsername) { this.schemaUsername = schemaUsername; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.password") public String getSchemaPassword() { return this.schemaPassword; } + @Deprecated public void setSchemaPassword(String schemaPassword) { this.schemaPassword = schemaPassword; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.dmlScriptLocations") public List getData() { return this.data; } + @Deprecated public void setData(List data) { this.data = data; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.username") public String getDataUsername() { return this.dataUsername; } + @Deprecated public void setDataUsername(String dataUsername) { this.dataUsername = dataUsername; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.password") public String getDataPassword() { return this.dataPassword; } + @Deprecated public void setDataPassword(String dataPassword) { this.dataPassword = dataPassword; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.continue-on-error") public boolean isContinueOnError() { return this.continueOnError; } + @Deprecated public void setContinueOnError(boolean continueOnError) { this.continueOnError = continueOnError; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.separator") public String getSeparator() { return this.separator; } + @Deprecated public void setSeparator(String separator) { this.separator = separator; } + @Deprecated + @DeprecatedConfigurationProperty(replacement = "spring.sql.init.encoding") public Charset getSqlScriptEncoding() { return this.sqlScriptEncoding; } + @Deprecated public void setSqlScriptEncoding(Charset sqlScriptEncoding) { this.sqlScriptEncoding = sqlScriptEncoding; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.java new file mode 100644 index 0000000000..87b864e781 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2021 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 org.springframework.boot.autoconfigure.sql.init; + +import java.util.ArrayList; +import java.util.List; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +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.jdbc.DataSourceBuilder; +import org.springframework.boot.jdbc.init.DataSourceInitializationSettings; +import org.springframework.boot.jdbc.init.ScriptDataSourceInitializer; +import org.springframework.boot.jdbc.init.dependency.DataSourceInitializationDependencyConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.jdbc.datasource.SimpleDriverDataSource; +import org.springframework.util.StringUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for initializing an SQL database. + * + * @author Andy Wilkinson + * @since 2.5.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnMissingBean(ScriptDataSourceInitializer.class) +@ConditionalOnSingleCandidate(DataSource.class) +@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true) +@AutoConfigureAfter(DataSourceAutoConfiguration.class) +@EnableConfigurationProperties(SqlInitializationProperties.class) +@Import(DataSourceInitializationDependencyConfigurer.class) +public class SqlInitializationAutoConfiguration { + + @Bean + ScriptDataSourceInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource, + SqlInitializationProperties initializationProperties) { + DataSourceInitializationSettings settings = createSettings(initializationProperties); + return new ScriptDataSourceInitializer(determineDataSource(dataSource, initializationProperties.getUsername(), + initializationProperties.getPassword()), settings); + } + + private static DataSourceInitializationSettings createSettings(SqlInitializationProperties properties) { + DataSourceInitializationSettings settings = new DataSourceInitializationSettings(); + settings.setSchemaLocations( + scriptLocations(properties.getSchemaLocations(), "schema", properties.getPlatform())); + settings.setDataLocations(scriptLocations(properties.getDataLocations(), "data", properties.getPlatform())); + settings.setContinueOnError(properties.isContinueOnError()); + settings.setSeparator(properties.getSeparator()); + settings.setEncoding(properties.getEncoding()); + return settings; + } + + private static List scriptLocations(List locations, String fallback, String platform) { + if (locations != null) { + return locations; + } + List fallbackLocations = new ArrayList<>(); + fallbackLocations.add("optional:classpath*:" + fallback + "-" + platform + ".sql"); + fallbackLocations.add("optional:classpath*:" + fallback + ".sql"); + return fallbackLocations; + } + + private static DataSource determineDataSource(DataSource dataSource, String username, String password) { + if (StringUtils.hasText(username) && StringUtils.hasText(password)) { + DataSourceBuilder.derivedFrom(dataSource).username(username).password(password) + .type(SimpleDriverDataSource.class).build(); + } + return dataSource; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.java new file mode 100644 index 0000000000..174f4ee3c8 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.java @@ -0,0 +1,141 @@ +/* + * Copyright 2012-2021 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 org.springframework.boot.autoconfigure.sql.init; + +import java.nio.charset.Charset; +import java.util.List; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * {@link ConfigurationProperties Configuration properties} for initializing an SQL + * database. + * + * @author Andy Wilkinson + * @since 2.5.0 + */ +@ConfigurationProperties("spring.sql.init") +public class SqlInitializationProperties { + + /** + * Locations of the schema (DDL) scripts to apply to the database. + */ + private List schemaLocations; + + /** + * Locations of the data (DML) scripts to apply to the database. + */ + private List dataLocations; + + /** + * Platform to use in the default schema or data script locations, + * schema-${platform}.sql and data-${platform}.sql. + */ + private String platform = "all"; + + /** + * Username of the database to use when applying initialization scripts (if + * different). + */ + private String username; + + /** + * Password of the database to use when applying initialization scripts (if + * different). + */ + private String password; + + /** + * Whether initialization should continue when an error occurs. + */ + private boolean continueOnError = false; + + /** + * Statement separator in the schema and data scripts. + */ + private String separator = ";"; + + /** + * Encoding of the schema and data scripts. + */ + private Charset encoding; + + public List getSchemaLocations() { + return this.schemaLocations; + } + + public void setSchemaLocations(List schemaLocations) { + this.schemaLocations = schemaLocations; + } + + public List getDataLocations() { + return this.dataLocations; + } + + public void setDataLocations(List dataLocations) { + this.dataLocations = dataLocations; + } + + public String getPlatform() { + return this.platform; + } + + public void setPlatform(String platform) { + this.platform = platform; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isContinueOnError() { + return this.continueOnError; + } + + public void setContinueOnError(boolean continueOnError) { + this.continueOnError = continueOnError; + } + + public String getSeparator() { + return this.separator; + } + + public void setSeparator(String separator) { + this.separator = separator; + } + + public Charset getEncoding() { + return this.encoding; + } + + public void setEncoding(Charset encoding) { + this.encoding = encoding; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/package-info.java new file mode 100644 index 0000000000..cfbdf3cd78 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2021 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. + */ + +/** + * Auto-configuration for basic script-based initialization of an SQL database. + */ +package org.springframework.boot.autoconfigure.sql.init; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 761e4c0efd..3298f297dd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1742,6 +1742,12 @@ "request" ] }, + { + "name": "spring.sql.init.enabled", + "type": "java.lang.Boolean", + "description": "Whether basic script-based initialization of an SQL database is enabled.", + "defaultValue": true + }, { "name": "spring.thymeleaf.prefix", "defaultValue": "classpath:/templates/" diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 446a139d16..df1cb2d437 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -127,6 +127,7 @@ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveO org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ +org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\ org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ @@ -173,4 +174,4 @@ org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvid # DataSource initializer detectors org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector=\ -org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializerDataSourceInitializerDetector \ No newline at end of file +org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializerDataSourceInitializerDetector diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunnerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunnerTests.java index 20eccd3719..b80bedb45d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunnerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunnerTests.java @@ -68,8 +68,7 @@ class JobLauncherApplicationRunnerTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration( AutoConfigurations.of(DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class)) - .withUserConfiguration(BatchConfiguration.class) - .withPropertyValues("spring.datasource.initialization-mode=never"); + .withUserConfiguration(BatchConfiguration.class); @Test void basicExecution() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java index 6eeaf9a97d..2fbbac6c22 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -48,8 +48,8 @@ class CouchbaseReactiveAndImperativeRepositoriesAutoConfigurationTests { void shouldCreateInstancesForReactiveAndImperativeRepositories() { new ApplicationContextRunner() .withUserConfiguration(ImperativeAndReactiveConfiguration.class, BaseConfiguration.class) - .withPropertyValues("spring.datasource.initialization-mode:never").run((context) -> assertThat(context) - .hasSingleBean(CityRepository.class).hasSingleBean(ReactiveCityRepository.class)); + .run((context) -> assertThat(context).hasSingleBean(CityRepository.class) + .hasSingleBean(ReactiveCityRepository.class)); } @Configuration(proxyBeanMethods = false) 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 9fa77bb130..ffc40727b2 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -30,6 +30,7 @@ import org.springframework.boot.autoconfigure.data.jdbc.city.CityRepository; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; 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.Configuration; import org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration; @@ -111,9 +112,12 @@ class JdbcRepositoriesAutoConfigurationTests { } private Function database() { - return (runner) -> runner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.schema=classpath:data-city-schema.sql", - "spring.datasource.data=classpath:city.sql", "spring.datasource.generate-unique-name:true"); + return (runner) -> runner + .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, + SqlInitializationAutoConfiguration.class)) + .withPropertyValues("spring.sql.init.schema-locations=classpath:data-city-schema.sql", + "spring.sql.init.data-locations=classpath:city.sql", + "spring.datasource.generate-unique-name:true"); } @TestAutoConfigurationPackage(City.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java index e58cebd935..3f6800aa2f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -62,7 +62,6 @@ class MixedMongoRepositoriesAutoConfigurationTests { @Test void testDefaultRepositoryConfiguration() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.datasource.initialization-mode:never").applyTo(this.context); this.context.register(TestConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CountryRepository.class)).isNotNull(); @@ -71,7 +70,6 @@ class MixedMongoRepositoriesAutoConfigurationTests { @Test void testMixedRepositoryConfiguration() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.datasource.initialization-mode:never").applyTo(this.context); this.context.register(MixedConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CountryRepository.class)).isNotNull(); @@ -81,7 +79,6 @@ class MixedMongoRepositoriesAutoConfigurationTests { @Test void testJpaRepositoryConfigurationWithMongoTemplate() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.datasource.initialization-mode:never").applyTo(this.context); this.context.register(JpaConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); @@ -90,7 +87,6 @@ class MixedMongoRepositoriesAutoConfigurationTests { @Test void testJpaRepositoryConfigurationWithMongoOverlap() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.datasource.initialization-mode:never").applyTo(this.context); this.context.register(OverlapConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); @@ -99,9 +95,7 @@ class MixedMongoRepositoriesAutoConfigurationTests { @Test void testJpaRepositoryConfigurationWithMongoOverlapDisabled() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues - .of("spring.datasource.initialization-mode:never", "spring.data.mongodb.repositories.type:none") - .applyTo(this.context); + TestPropertyValues.of("spring.data.mongodb.repositories.type:none").applyTo(this.context); this.context.register(OverlapConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveAndBlockingRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveAndBlockingRepositoriesAutoConfigurationTests.java index a197912c0e..4ac412c659 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveAndBlockingRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoReactiveAndBlockingRepositoriesAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.data.mongo.city.CityRepository; import org.springframework.boot.autoconfigure.data.mongo.city.ReactiveCityRepository; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration; -import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -57,7 +56,6 @@ class MongoReactiveAndBlockingRepositoriesAutoConfigurationTests { @Test void shouldCreateInstancesForReactiveAndBlockingRepositories() { this.context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of("spring.datasource.initialization-mode:never").applyTo(this.context); this.context.register(BlockingAndReactiveConfiguration.class, BaseConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(CityRepository.class)).isNotNull(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java index eb949d32fd..56d194b1ba 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -99,7 +99,7 @@ class MixedNeo4jRepositoriesAutoConfigurationTests { private void load(Class config, String... environment) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - TestPropertyValues.of(environment).and("spring.datasource.initialization-mode=never").applyTo(context); + TestPropertyValues.of(environment).applyTo(context); context.register(config); context.register(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class, Neo4jDataAutoConfiguration.class, diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastJpaDependencyAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastJpaDependencyAutoConfigurationTests.java index a8a177c198..c50db4119d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastJpaDependencyAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/hazelcast/HazelcastJpaDependencyAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -51,8 +51,7 @@ class HazelcastJpaDependencyAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, HazelcastJpaDependencyAutoConfiguration.class)) - .withPropertyValues("spring.datasource.generate-unique-name=true", - "spring.datasource.initialization-mode=never"); + .withPropertyValues("spring.datasource.generate-unique-name=true"); @Test void registrationIfHazelcastInstanceHasRegularBeanName() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java index fd2b0d6238..8010165e47 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfigurationTests.java @@ -43,6 +43,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.jdbc.EmbeddedDatabaseConnection; +import org.springframework.boot.jdbc.init.ScriptDataSourceInitializer; import org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitialization; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; @@ -67,8 +68,7 @@ class DataSourceAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.initialization-mode=never", - "spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt()); + .withPropertyValues("spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt()); @Test void testDefaultDataSourceExists() { @@ -236,11 +236,18 @@ class DataSourceAutoConfigurationTests { } @Test + @Deprecated void testDataSourceIsInitializedEarly() { this.contextRunner.withUserConfiguration(TestInitializedDataSourceConfiguration.class) - .withPropertyValues("spring.datasource.initialization-mode=always") - .run((context) -> assertThat(context.getBean(TestInitializedDataSourceConfiguration.class).called) - .isTrue()); + .withPropertyValues("spring.datasource.initialization-mode=always").run((context) -> { + assertThat(context).hasSingleBean(ScriptDataSourceInitializer.class); + assertThat(context.getBean(TestInitializedDataSourceConfiguration.class).called).isTrue(); + }); + } + + @Test + void whenNoInitializationRelatedSpringDataSourcePropertiesAreConfiguredThenInitializationBacksOff() { + this.contextRunner.run((context) -> assertThat(context).doesNotHaveBean(ScriptDataSourceInitializer.class)); } private static Function hideConnectionPools() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationIntegrationTests.java index 90f980f0ae..8a9bf3951b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationIntegrationTests.java @@ -66,6 +66,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Dave Syer * @author Stephane Nicoll */ +@Deprecated class DataSourceInitializationIntegrationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfigurationTests.java index a1d3835a01..175925fa10 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -63,7 +63,11 @@ class DataSourceJmxConfigurationTests { "spring.datasource.name=" + poolName, "spring.datasource.hikari.register-mbeans=true") .run((context) -> { assertThat(context).hasSingleBean(HikariDataSource.class); - assertThat(context.getBean(HikariDataSource.class).isRegisterMbeans()).isTrue(); + HikariDataSource hikariDataSource = context.getBean(HikariDataSource.class); + assertThat(hikariDataSource.isRegisterMbeans()).isTrue(); + // Ensure that the pool has been initialized, triggering MBean + // registration + hikariDataSource.getConnection().close(); MBeanServer mBeanServer = context.getBean(MBeanServer.class); validateHikariMBeansRegistration(mBeanServer, poolName, true); }); @@ -77,23 +81,30 @@ class DataSourceJmxConfigurationTests { this.contextRunner.withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName(), "spring.datasource.hikari.register-mbeans=true").run((context) -> { assertThat(context).hasSingleBean(HikariDataSource.class); - assertThat(context.getBean(HikariDataSource.class).isRegisterMbeans()).isTrue(); + HikariDataSource hikariDataSource = context.getBean(HikariDataSource.class); + assertThat(hikariDataSource.isRegisterMbeans()).isTrue(); + // Ensure that the pool has been initialized, triggering MBean + // registration + hikariDataSource.getConnection().close(); // We can't rely on the number of MBeans so we're checking that the - // pool and pool - // config MBeans were registered + // pool and pool config MBeans were registered assertThat(mBeanServer.queryMBeans(new ObjectName("com.zaxxer.hikari:type=*"), null).size()) .isEqualTo(existingInstances.size() + 2); }); } @Test - void hikariAutoConfiguredUsesJmsFlag() { + void hikariAutoConfiguredUsesJmxFlag() { String poolName = UUID.randomUUID().toString(); this.contextRunner.withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName(), "spring.jmx.enabled=false", "spring.datasource.name=" + poolName, "spring.datasource.hikari.register-mbeans=true").run((context) -> { assertThat(context).hasSingleBean(HikariDataSource.class); - assertThat(context.getBean(HikariDataSource.class).isRegisterMbeans()).isTrue(); + HikariDataSource hikariDataSource = context.getBean(HikariDataSource.class); + assertThat(hikariDataSource.isRegisterMbeans()).isTrue(); + // Ensure that the pool has been initialized, triggering MBean + // registration + hikariDataSource.getConnection().close(); // Hikari can still register mBeans validateHikariMBeansRegistration(ManagementFactory.getPlatformMBeanServer(), poolName, true); }); @@ -111,6 +122,9 @@ class DataSourceJmxConfigurationTests { HikariDataSource hikariDataSource = context.getBean(javax.sql.DataSource.class) .unwrap(HikariDataSource.class); assertThat(hikariDataSource.isRegisterMbeans()).isTrue(); + // Ensure that the pool has been initialized, triggering MBean + // registration + hikariDataSource.getConnection().close(); MBeanServer mBeanServer = context.getBean(MBeanServer.class); validateHikariMBeansRegistration(mBeanServer, poolName, true); }); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java index cfa2769395..623891402c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -161,6 +161,7 @@ class DataSourcePropertiesTests { } @Test + @Deprecated void determineCredentialsForSchemaScripts() { DataSourceProperties properties = new DataSourceProperties(); properties.setSchemaUsername("foo"); @@ -170,6 +171,7 @@ class DataSourcePropertiesTests { } @Test + @Deprecated void determineCredentialsForDataScripts() { DataSourceProperties properties = new DataSourceProperties(); properties.setDataUsername("foo"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java index a3643466d8..ebd28e9cfb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDataSourceConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -36,8 +36,7 @@ class HikariDataSourceConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.initialization-mode=never", - "spring.datasource.type=" + HikariDataSource.class.getName()); + .withPropertyValues("spring.datasource.type=" + HikariDataSource.class.getName()); @Test void testDataSourceExists() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDriverConfigurationFailureAnalyzerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDriverConfigurationFailureAnalyzerTests.java index c75ee30c51..83add1d50a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDriverConfigurationFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDriverConfigurationFailureAnalyzerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -60,8 +61,7 @@ class HikariDriverConfigurationFailureAnalyzerTests { private BeanCreationException createFailure(Class configuration) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); TestPropertyValues.of("spring.datasource.type=" + HikariDataSource.class.getName(), - "spring.datasource.hikari.data-source-class-name=com.example.Foo", - "spring.datasource.initialization-mode=always").applyTo(context); + "spring.datasource.hikari.data-source-class-name=com.example.Foo").applyTo(context); context.register(configuration); try { context.refresh(); @@ -74,7 +74,7 @@ class HikariDriverConfigurationFailureAnalyzerTests { } @Configuration(proxyBeanMethods = false) - @ImportAutoConfiguration(DataSourceAutoConfiguration.class) + @ImportAutoConfiguration({ DataSourceAutoConfiguration.class, SqlInitializationAutoConfiguration.class }) static class TestConfiguration { } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JdbcTemplateAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JdbcTemplateAutoConfigurationTests.java index 2ac583fe1a..5f51ab8b73 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JdbcTemplateAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JdbcTemplateAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +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; @@ -48,9 +49,7 @@ import static org.mockito.Mockito.mock; class JdbcTemplateAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withPropertyValues("spring.datasource.initialization-mode=never", - "spring.datasource.generate-unique-name=true") - .withConfiguration( + .withPropertyValues("spring.datasource.generate-unique-name=true").withConfiguration( AutoConfigurations.of(DataSourceAutoConfiguration.class, JdbcTemplateAutoConfiguration.class)); @Test @@ -147,7 +146,8 @@ class JdbcTemplateAutoConfigurationTests { } @Test - void testDependencyToDataSourceInitialization() { + @Deprecated + void testDependencyToDeprecatedDataSourceInitialization() { this.contextRunner.withUserConfiguration(DataSourceInitializationValidator.class) .withPropertyValues("spring.datasource.initialization-mode=always").run((context) -> { assertThat(context).hasNotFailed(); @@ -155,6 +155,15 @@ class JdbcTemplateAutoConfigurationTests { }); } + @Test + void testDependencyToScriptBasedDataSourceInitialization() { + this.contextRunner.withConfiguration(AutoConfigurations.of(SqlInitializationAutoConfiguration.class)) + .withUserConfiguration(DataSourceInitializationValidator.class).run((context) -> { + assertThat(context).hasNotFailed(); + assertThat(context.getBean(DataSourceInitializationValidator.class).count).isEqualTo(1); + }); + } + @Test void testDependencyToFlyway() { this.contextRunner.withUserConfiguration(DataSourceMigrationValidator.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java index 93df579d57..353b2777d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/OracleUcpDataSourceConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -39,8 +39,7 @@ class OracleUcpDataSourceConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class)) - .withPropertyValues("spring.datasource.initialization-mode=never", - "spring.datasource.type=" + PoolDataSource.class.getName()); + .withPropertyValues("spring.datasource.type=" + PoolDataSource.class.getName()); @Test void testDataSourceExists() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java index 167cb8cc98..b183db6205 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/AbstractJpaAutoConfigurationTests.java @@ -32,6 +32,7 @@ import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.test.City; +import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration; import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.test.context.assertj.AssertableApplicationContext; @@ -71,8 +72,9 @@ abstract class AbstractJpaAutoConfigurationTests { this.autoConfiguredClass = autoConfiguredClass; this.contextRunner = new ApplicationContextRunner() .withPropertyValues("spring.datasource.generate-unique-name=true") - .withUserConfiguration(TestConfiguration.class).withConfiguration(AutoConfigurations.of( - DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class, autoConfiguredClass)); + .withUserConfiguration(TestConfiguration.class).withConfiguration( + AutoConfigurations.of(DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class, + SqlInitializationAutoConfiguration.class, autoConfiguredClass)); } protected ApplicationContextRunner contextRunner() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/Hibernate2ndLevelCacheIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/Hibernate2ndLevelCacheIntegrationTests.java index fe83c44833..049ae5abe9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/Hibernate2ndLevelCacheIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/Hibernate2ndLevelCacheIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 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. @@ -38,7 +38,6 @@ class Hibernate2ndLevelCacheIntegrationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(CacheAutoConfiguration.class, DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class)) - .withPropertyValues("spring.datasource.initialization-mode=never") .withUserConfiguration(TestConfiguration.class); @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java index 96bd4909ec..233440d36a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java @@ -98,7 +98,8 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes } @Test - void testDataScriptWithMissingDdl() { + @Deprecated + void testDataScriptWithDeprecatedMissingDdl() { contextRunner().withPropertyValues("spring.datasource.data:classpath:/city.sql", // Missing: "spring.datasource.schema:classpath:/ddl.sql").run((context) -> { @@ -107,6 +108,16 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes }); } + @Test + void testDmlScriptWithMissingDdl() { + contextRunner().withPropertyValues("spring.sql.init.data-locations:classpath:/city.sql", + // Missing: + "spring.sql.init.schema-locations:classpath:/ddl.sql").run((context) -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()).hasMessageContaining("ddl.sql"); + }); + } + @Test void testDataScript() { // This can't succeed because the data SQL is executed immediately after the @@ -118,6 +129,17 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes } @Test + void testDmlScript() { + // This can't succeed because the data SQL is executed immediately after the + // schema and Hibernate hasn't initialized yet at that point + contextRunner().withPropertyValues("spring.sql.init.data-locations:/city.sql").run((context) -> { + assertThat(context).hasFailed(); + assertThat(context.getStartupFailure()).isInstanceOf(BeanCreationException.class); + }); + } + + @Test + @Deprecated void testDataScriptRunsEarly() { contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class) .withClassLoader(new HideDataScriptClassLoader()) @@ -126,11 +148,18 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes .run((context) -> assertThat(context.getBean(TestInitializedJpaConfiguration.class).called).isTrue()); } + @Test + void testDmlScriptRunsEarly() { + contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class) + .withClassLoader(new HideDataScriptClassLoader()) + .withPropertyValues("spring.jpa.show-sql=true", "spring.jpa.hibernate.ddl-auto:create-drop", + "spring.sql.init.data-locations:/city.sql", "spring.jpa.defer-datasource-initialization=true") + .run((context) -> assertThat(context.getBean(TestInitializedJpaConfiguration.class).called).isTrue()); + } + @Test void testFlywaySwitchOffDdlAuto() { - contextRunner() - .withPropertyValues("spring.datasource.initialization-mode:never", - "spring.flyway.locations:classpath:db/city") + contextRunner().withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city") .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class)) .run((context) -> assertThat(context).hasNotFailed()); } @@ -138,8 +167,8 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes @Test void testFlywayPlusValidation() { contextRunner() - .withPropertyValues("spring.datasource.initialization-mode:never", - "spring.flyway.locations:classpath:db/city", "spring.jpa.hibernate.ddl-auto:validate") + .withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city", + "spring.jpa.hibernate.ddl-auto:validate") .withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class)) .run((context) -> assertThat(context).hasNotFailed()); } @@ -147,8 +176,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes @Test void testLiquibasePlusValidation() { contextRunner() - .withPropertyValues("spring.datasource.initialization-mode:never", - "spring.liquibase.changeLog:classpath:db/changelog/db.changelog-city.yaml", + .withPropertyValues("spring.liquibase.changeLog:classpath:db/changelog/db.changelog-city.yaml", "spring.jpa.hibernate.ddl-auto:validate") .withConfiguration(AutoConfigurations.of(LiquibaseAutoConfiguration.class)) .run((context) -> assertThat(context).hasNotFailed()); @@ -283,7 +311,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes @Test void customResourceMapping() { contextRunner().withClassLoader(new HideDataScriptClassLoader()) - .withPropertyValues("spring.datasource.data:classpath:/db/non-annotated-data.sql", + .withPropertyValues("spring.sql.init.data-locations:classpath:/db/non-annotated-data.sql", "spring.jpa.mapping-resources=META-INF/mappings/non-annotated.xml", "spring.jpa.defer-datasource-initialization=true") .run((context) -> { @@ -357,7 +385,8 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes contextRunner().withUserConfiguration(TestInitializedJpaConfiguration.class) .withClassLoader(new HideDataScriptClassLoader()) .withPropertyValues("spring.jpa.show-sql=true", "spring.jpa.hibernate.ddl-auto:create-drop", - "spring.datasource.data:classpath:/city.sql", "spring.jpa.defer-datasource-initialization=true") + "spring.sql.init.data-locations:classpath:/city.sql", + "spring.jpa.defer-datasource-initialization=true") .run((context) -> { // See CityListener assertThat(context).hasSingleBean(City.class); @@ -435,22 +464,18 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes @Test void withSyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException() { - contextRunner().withUserConfiguration(JpaUsingApplicationListenerConfiguration.class) - .withPropertyValues("spring.datasource.initialization-mode=never").run((context) -> { - assertThat(context).hasNotFailed(); - EventCapturingApplicationListener listener = context - .getBean(EventCapturingApplicationListener.class); - assertThat(listener.events).hasSize(1); - assertThat(listener.events).hasOnlyElementsOfType(ContextRefreshedEvent.class); - }); + contextRunner().withUserConfiguration(JpaUsingApplicationListenerConfiguration.class).run((context) -> { + assertThat(context).hasNotFailed(); + EventCapturingApplicationListener listener = context.getBean(EventCapturingApplicationListener.class); + assertThat(listener.events).hasSize(1); + assertThat(listener.events).hasOnlyElementsOfType(ContextRefreshedEvent.class); + }); } @Test void withAsyncBootstrappingAnApplicationListenerThatUsesJpaDoesNotTriggerABeanCurrentlyInCreationException() { - contextRunner() - .withUserConfiguration(AsyncBootstrappingConfiguration.class, - JpaUsingApplicationListenerConfiguration.class) - .withPropertyValues("spring.datasource.initialization-mode=never").run((context) -> { + contextRunner().withUserConfiguration(AsyncBootstrappingConfiguration.class, + JpaUsingApplicationListenerConfiguration.class).run((context) -> { assertThat(context).hasNotFailed(); EventCapturingApplicationListener listener = context .getBean(EventCapturingApplicationListener.class); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfigurationTests.java index 3f4e72a1d1..eecf08ae28 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 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. @@ -149,9 +149,7 @@ class SecurityAutoConfigurationTests { @Test void testJpaCoexistsHappily() { - this.contextRunner - .withPropertyValues("spring.datasource.url:jdbc:hsqldb:mem:testsecdb", - "spring.datasource.initialization-mode:never") + this.contextRunner.withPropertyValues("spring.datasource.url:jdbc:hsqldb:mem:testsecdb") .withUserConfiguration(EntityConfiguration.class) .withConfiguration( AutoConfigurations.of(HibernateJpaAutoConfiguration.class, DataSourceAutoConfiguration.class)) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc index 2605d2adf2..fb1c70bfaa 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc @@ -2026,20 +2026,14 @@ It is a Hibernate feature (and has nothing to do with Spring). === Initialize a Database using basic SQL scripts Spring Boot can automatically create the schema (DDL scripts) of your `DataSource` and initialize it (DML scripts). It loads SQL from the standard root classpath locations: `schema.sql` and `data.sql`, respectively. -In addition, Spring Boot processes the `schema-$\{platform}.sql` and `data-$\{platform}.sql` files (if present), where `platform` is the value of `spring.datasource.platform`. +In addition, Spring Boot processes the `schema-$\{platform}.sql` and `data-$\{platform}.sql` files (if present), where `platform` is the value of configprop:spring.sql.init.platform[]. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (`hsqldb`, `h2`, `oracle`, `mysql`, `postgresql`, and so on). [NOTE] ==== -When only basic SQL scripts are used, Spring Boot automatically creates the schema of an embedded `DataSource`. -This behavior can be customized by using the configprop:spring.datasource.initialization-mode[] property. -For instance, if you want to always initialize the `DataSource` regardless of its type: - -[indent=0,subs="verbatim,quotes,attributes"] ----- - spring.datasource.initialization-mode=always ----- +When only basic SQL scripts are used, Spring Boot automatically initializes the `DataSource`. +This initialization can be disabled by setting the configprop:spring.sql.init.enabled[] property to `false`. By default, script-based `DataSource` initialization is performed before any JPA `EntityManagerFactory` beans are created. `schema.sql` can be used to create the schema for JPA-managed entities and `data.sql` can be used to populate it. @@ -2054,7 +2048,7 @@ Using the basic `schema.sql` and `data.sql` scripts alongside Flyway or Liquibas By default, Spring Boot enables the fail-fast feature of the Spring JDBC initializer. This means that, if the scripts cause exceptions, the application fails to start. -You can tune that behavior by setting `spring.datasource.continue-on-error`. +You can tune that behavior by setting configprop:spring.sql.init.continue-on-error[]. To take complete control over the script-based initialization of a `DataSource`, define your own `ScriptDataSourceInitializer` bean. Doing so will cause the auto-configuration of script-based initialization to back off.