Skip to content

Commit

Permalink
Move DataSource init auto-config out of DataSourceAutoConfiguration
Browse files Browse the repository at this point in the history
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
  • Loading branch information
wilkinsona committed Mar 24, 2021
1 parent e2811ac commit 90b4ced
Show file tree
Hide file tree
Showing 33 changed files with 533 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void documentConfigurationProperties() throws IOException {
.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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -33,6 +33,7 @@
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;
Expand Down Expand Up @@ -101,7 +102,8 @@ void autoConfiguredHikariDataSourceIsInstrumented() {
}

@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();
Expand All @@ -110,6 +112,17 @@ void autoConfiguredHikariDataSourceIsInstrumentedWhenUsingDataSourceInitializati
});
}

@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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -37,6 +37,7 @@
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;
Expand Down Expand Up @@ -132,9 +133,9 @@ void entityManagerFactoryInstrumentationIsDisabledIfHibernateIsNotAvailable() {

@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) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;

import javax.sql.DataSource;

Expand All @@ -30,6 +32,8 @@
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;
Expand Down Expand Up @@ -75,10 +79,8 @@ void liquibaseReportIsReturnedForContextHierarchy() {

@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);
Expand Down Expand Up @@ -138,6 +140,23 @@ LiquibaseEndpoint endpoint(ApplicationContext context) {

}

@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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
* @author Kazuki Shimizu
* @since 1.0.0
*/
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,39 @@

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;
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;

Expand All @@ -50,6 +60,7 @@
*
* @author Andy Wilkinson
*/
@Deprecated
class DataSourceInitializationConfiguration {

private static DataSource determineDataSource(Supplier<DataSource> dataSource, String username, String password,
Expand Down Expand Up @@ -128,10 +139,12 @@ static class DataCredentials {

}

@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
Expand All @@ -147,6 +160,32 @@ ScriptDataSourceInitializer scriptDataSourceInitializer(DataSource dataSource, D
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 {
Expand Down

0 comments on commit 90b4ced

Please sign in to comment.