Commit 90b4ced7 authored by Andy Wilkinson's avatar Andy Wilkinson

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
parent e2811ace
......@@ -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")
......
/*
* 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() {
......
/*
* 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)
......
/*
* 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) -> {
......
......@@ -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<String, LiquibaseBean> 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 {
......
......@@ -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")
......
......@@ -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> 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<String> 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<String> 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 {
......
/*
* 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<String> 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<String> getSchema() {
return this.schema;
}
@Deprecated
public void setSchema(List<String> 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<String> getData() {
return this.data;
}
@Deprecated
public void setData(List<String> 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;
}
......
/*
* 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<String> scriptLocations(List<String> locations, String fallback, String platform) {
if (locations != null) {
return locations;
}
List<String> 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;
}
}
/*
* 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<String> schemaLocations;
/**
* Locations of the data (DML) scripts to apply to the database.
*/
private List<String> 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<String> getSchemaLocations() {
return this.schemaLocations;
}
public void setSchemaLocations(List<String> schemaLocations) {
this.schemaLocations = schemaLocations;
}
public List<String> getDataLocations() {
return this.dataLocations;
}
public void setDataLocations(List<String> 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;
}
}
/*
* 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;
......@@ -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/"
......
......@@ -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
......@@ -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() {
......
/*
* 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)
......
/*
* 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<ApplicationContextRunner, ApplicationContextRunner> 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)
......
/*
* 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();
......
/*
* 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();
......
/*
* 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,
......
/*
* 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() {
......
......@@ -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<ApplicationContextRunner, ApplicationContextRunner> hideConnectionPools() {
......
......@@ -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()
......
/*
* 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);
});
......
/*
* 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");
......
/*
* 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() {
......
/*
* 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 {
}
......
/*
* 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)
......
/*
* 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() {
......
......@@ -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() {
......
/*
* 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
......
......@@ -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);
......
/*
* 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))
......
......@@ -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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment