Skip to content

Commit c521437

Browse files
committed
Reinstate mode for controlling DB initialization
Closes gh-26682
1 parent 1a0e008 commit c521437

22 files changed

+429
-75
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceInitializationConfiguration.java

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.boot.autoconfigure.jdbc;
1818

19-
import java.nio.charset.Charset;
2019
import java.util.ArrayList;
2120
import java.util.Arrays;
2221
import java.util.Collections;
@@ -40,15 +39,14 @@
4039
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.DataSourceInitializationCondition;
4140
import org.springframework.boot.jdbc.DataSourceBuilder;
4241
import org.springframework.boot.jdbc.DataSourceInitializationMode;
43-
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
4442
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
43+
import org.springframework.boot.sql.init.DatabaseInitializationMode;
4544
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
4645
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
4746
import org.springframework.context.annotation.Bean;
4847
import org.springframework.context.annotation.ConditionContext;
4948
import org.springframework.context.annotation.DependsOn;
5049
import org.springframework.core.env.Environment;
51-
import org.springframework.core.io.Resource;
5250
import org.springframework.core.type.AnnotatedTypeMetadata;
5351
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
5452
import org.springframework.util.StringUtils;
@@ -80,6 +78,19 @@ private static List<String> scriptLocations(List<String> locations, String fallb
8078
return fallbackLocations;
8179
}
8280

81+
private static DatabaseInitializationMode mapMode(DataSourceInitializationMode mode) {
82+
switch (mode) {
83+
case ALWAYS:
84+
return DatabaseInitializationMode.ALWAYS;
85+
case EMBEDDED:
86+
return DatabaseInitializationMode.EMBEDDED;
87+
case NEVER:
88+
return DatabaseInitializationMode.NEVER;
89+
default:
90+
throw new IllegalStateException("Unexpected initialization mode '" + mode + "'");
91+
}
92+
}
93+
8394
// Fully-qualified to work around javac bug in JDK 1.8
8495
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
8596
@org.springframework.context.annotation.Conditional(DifferentCredentialsCondition.class)
@@ -96,10 +107,10 @@ DataSourceScriptDatabaseInitializer ddlOnlyScriptDataSourceInitializer(ObjectPro
96107
settings.setContinueOnError(properties.isContinueOnError());
97108
settings.setSeparator(properties.getSeparator());
98109
settings.setEncoding(properties.getSqlScriptEncoding());
110+
settings.setMode(mapMode(properties.getInitializationMode()));
99111
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
100112
properties.getSchemaUsername(), properties.getSchemaPassword());
101-
return new InitializationModeDataSourceScriptDatabaseInitializer(initializationDataSource, settings,
102-
properties.getInitializationMode());
113+
return new DataSourceScriptDatabaseInitializer(initializationDataSource, settings);
103114
}
104115

105116
@Bean
@@ -111,10 +122,10 @@ DataSourceScriptDatabaseInitializer dmlOnlyScriptDataSourceInitializer(ObjectPro
111122
settings.setContinueOnError(properties.isContinueOnError());
112123
settings.setSeparator(properties.getSeparator());
113124
settings.setEncoding(properties.getSqlScriptEncoding());
125+
settings.setMode(mapMode(properties.getInitializationMode()));
114126
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
115127
properties.getDataUsername(), properties.getDataPassword());
116-
return new InitializationModeDataSourceScriptDatabaseInitializer(initializationDataSource, settings,
117-
properties.getInitializationMode());
128+
return new DataSourceScriptDatabaseInitializer(initializationDataSource, settings);
118129
}
119130

120131
static class DifferentCredentialsCondition extends AnyNestedCondition {
@@ -154,8 +165,8 @@ DataSourceScriptDatabaseInitializer scriptDataSourceInitializer(DataSource dataS
154165
settings.setContinueOnError(properties.isContinueOnError());
155166
settings.setSeparator(properties.getSeparator());
156167
settings.setEncoding(properties.getSqlScriptEncoding());
157-
return new InitializationModeDataSourceScriptDatabaseInitializer(dataSource, settings,
158-
properties.getInitializationMode());
168+
settings.setMode(mapMode(properties.getInitializationMode()));
169+
return new DataSourceScriptDatabaseInitializer(dataSource, settings);
159170
}
160171

161172
static class DataSourceInitializationCondition extends SpringBootCondition {
@@ -186,25 +197,4 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM
186197

187198
}
188199

189-
static class InitializationModeDataSourceScriptDatabaseInitializer extends DataSourceScriptDatabaseInitializer {
190-
191-
private final DataSourceInitializationMode mode;
192-
193-
InitializationModeDataSourceScriptDatabaseInitializer(DataSource dataSource,
194-
DatabaseInitializationSettings settings, DataSourceInitializationMode mode) {
195-
super(dataSource, settings);
196-
this.mode = mode;
197-
}
198-
199-
@Override
200-
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator,
201-
Charset encoding) {
202-
if (this.mode == DataSourceInitializationMode.ALWAYS || (this.mode == DataSourceInitializationMode.EMBEDDED
203-
&& EmbeddedDatabaseConnection.isEmbedded(getDataSource()))) {
204-
super.runScripts(resources, continueOnError, separator, encoding);
205-
}
206-
}
207-
208-
}
209-
210200
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ public void setJndiName(String jndiName) {
392392
}
393393

394394
@Deprecated
395-
@DeprecatedConfigurationProperty(replacement = "spring.sql.init.enabled")
395+
@DeprecatedConfigurationProperty(replacement = "spring.sql.init.mode")
396396
public DataSourceInitializationMode getInitializationMode() {
397397
return this.initializationMode;
398398
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SettingsCreator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ static DatabaseInitializationSettings createFrom(SqlInitializationProperties pro
4141
settings.setContinueOnError(properties.isContinueOnError());
4242
settings.setSeparator(properties.getSeparator());
4343
settings.setEncoding(properties.getEncoding());
44+
settings.setMode(properties.getMode());
4445
return settings;
4546
}
4647

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2121
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
23+
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
2324
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
2425
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
26+
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration.SqlInitializationModeCondition;
2527
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2628
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
2729
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
30+
import org.springframework.context.annotation.Conditional;
2831
import org.springframework.context.annotation.Configuration;
2932
import org.springframework.context.annotation.Import;
3033

@@ -36,11 +39,25 @@
3639
*/
3740
@Configuration(proxyBeanMethods = false)
3841
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)
39-
@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
4042
@AutoConfigureAfter({ R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class })
4143
@EnableConfigurationProperties(SqlInitializationProperties.class)
4244
@Import({ DatabaseInitializationDependencyConfigurer.class, R2dbcInitializationConfiguration.class,
4345
DataSourceInitializationConfiguration.class })
46+
@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
47+
@Conditional(SqlInitializationModeCondition.class)
4448
public class SqlInitializationAutoConfiguration {
4549

50+
static class SqlInitializationModeCondition extends NoneNestedConditions {
51+
52+
SqlInitializationModeCondition() {
53+
super(ConfigurationPhase.PARSE_CONFIGURATION);
54+
}
55+
56+
@ConditionalOnProperty(prefix = "spring.sql.init", name = "mode", havingValue = "never")
57+
static class ModeIsNever {
58+
59+
}
60+
61+
}
62+
4663
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121

2222
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.boot.sql.init.DatabaseInitializationMode;
2324

2425
/**
2526
* {@link ConfigurationProperties Configuration properties} for initializing an SQL
@@ -74,6 +75,11 @@ public class SqlInitializationProperties {
7475
*/
7576
private Charset encoding;
7677

78+
/**
79+
* Mode to apply when determining whether initialization should be performed.
80+
*/
81+
private DatabaseInitializationMode mode = DatabaseInitializationMode.EMBEDDED;
82+
7783
public List<String> getSchemaLocations() {
7884
return this.schemaLocations;
7985
}
@@ -138,4 +144,12 @@ public void setEncoding(Charset encoding) {
138144
this.encoding = encoding;
139145
}
140146

147+
public DatabaseInitializationMode getMode() {
148+
return this.mode;
149+
}
150+
151+
public void setMode(DatabaseInitializationMode mode) {
152+
this.mode = mode;
153+
}
154+
141155
}

spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1746,7 +1746,11 @@
17461746
"name": "spring.sql.init.enabled",
17471747
"type": "java.lang.Boolean",
17481748
"description": "Whether basic script-based initialization of an SQL database is enabled.",
1749-
"defaultValue": true
1749+
"defaultValue": true,
1750+
"deprecation": {
1751+
"replacement": "spring.sql.init.mode",
1752+
"level": "error"
1753+
}
17501754
},
17511755
{
17521756
"name": "spring.thymeleaf.prefix",

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/HikariDriverConfigurationFailureAnalyzerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ private FailureAnalysis performAnalysis(Class<?> configuration) {
6161
private BeanCreationException createFailure(Class<?> configuration) {
6262
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
6363
TestPropertyValues.of("spring.datasource.type=" + HikariDataSource.class.getName(),
64-
"spring.datasource.hikari.data-source-class-name=com.example.Foo").applyTo(context);
64+
"spring.datasource.hikari.data-source-class-name=com.example.Foo", "spring.sql.init.mode=always")
65+
.applyTo(context);
6566
context.register(configuration);
6667
try {
6768
context.refresh();

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfigurationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,15 @@ void testDmlScriptRunsEarly() {
159159

160160
@Test
161161
void testFlywaySwitchOffDdlAuto() {
162-
contextRunner().withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city")
162+
contextRunner().withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city")
163163
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
164164
.run((context) -> assertThat(context).hasNotFailed());
165165
}
166166

167167
@Test
168168
void testFlywayPlusValidation() {
169169
contextRunner()
170-
.withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city",
170+
.withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city",
171171
"spring.jpa.hibernate.ddl-auto:validate")
172172
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
173173
.run((context) -> assertThat(context).hasNotFailed());

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfigurationTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
import org.springframework.beans.factory.config.BeanDefinition;
2828
import org.springframework.boot.autoconfigure.AutoConfigurations;
2929
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
30+
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
3031
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
3132
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
33+
import org.springframework.boot.logging.LogLevel;
3234
import org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializer;
3335
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
3436
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
@@ -64,25 +66,42 @@ void whenConnectionFactoryIsAvailableThenR2dbcInitializerIsAutoConfigured() {
6466
}
6567

6668
@Test
69+
@Deprecated
6770
void whenConnectionFactoryIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
6871
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
6972
.withPropertyValues("spring.sql.init.enabled:false")
7073
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
7174
}
7275

76+
@Test
77+
void whenConnectionFactoryIsAvailableAndModeIsNeverThenInitializerIsNotAutoConfigured() {
78+
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
79+
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
80+
.withPropertyValues("spring.sql.init.mode:never")
81+
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
82+
}
83+
7384
@Test
7485
void whenDataSourceIsAvailableThenDataSourceInitializerIsAutoConfigured() {
7586
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
7687
.run((context) -> assertThat(context).hasSingleBean(DataSourceScriptDatabaseInitializer.class));
7788
}
7889

7990
@Test
91+
@Deprecated
8092
void whenDataSourceIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
8193
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
8294
.withPropertyValues("spring.sql.init.enabled:false")
8395
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
8496
}
8597

98+
@Test
99+
void whenDataSourceIsAvailableAndModeIsNeverThenThenInitializerIsNotAutoConfigured() {
100+
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
101+
.withPropertyValues("spring.sql.init.mode:never")
102+
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
103+
}
104+
86105
@Test
87106
void whenDataSourceAndConnectionFactoryAreAvailableThenOnlyR2dbcInitializerIsAutoConfigured() {
88107
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
@@ -135,6 +154,11 @@ protected void runScripts(List<Resource> resources, boolean continueOnError, Str
135154
// No-op
136155
}
137156

157+
@Override
158+
protected boolean isEmbeddedDatabase() {
159+
return true;
160+
}
161+
138162
};
139163
}
140164

spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto/data-initialization.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ It loads SQL from the standard root classpath locations: `schema.sql` and `data.
4343
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[].
4444
This allows you to switch to database-specific scripts if necessary.
4545
For example, you might choose to set it to the vendor name of the database (`hsqldb`, `h2`, `oracle`, `mysql`, `postgresql`, and so on).
46-
SQL database initialization can be disabled by setting configprop:spring.sql.init.enabled[] to `false`.
46+
By default, SQL database initialization is only performed when using an embedded in-memory database.
47+
To always initialize an SQL database, irrespective of its type, set configprop:spring.sql.init.mode[] to `always`.
48+
Similarly, to disable initialization, set configprop:spring.sql.init.mode[] to `never`.
4749
By default, Spring Boot enables the fail-fast feature of its script-based database initializer.
4850
This means that, if the scripts cause exceptions, the application fails to start.
4951
You can tune that behavior by setting configprop:spring.sql.init.continue-on-error[].

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/init/DataSourceScriptDatabaseInitializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.sql.DataSource;
2323

2424
import org.springframework.beans.factory.InitializingBean;
25+
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
2526
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
2627
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
2728
import org.springframework.core.io.Resource;
@@ -58,6 +59,11 @@ protected final DataSource getDataSource() {
5859
return this.dataSource;
5960
}
6061

62+
@Override
63+
protected boolean isEmbeddedDatabase() {
64+
return EmbeddedDatabaseConnection.isEmbedded(this.dataSource);
65+
}
66+
6167
@Override
6268
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator, Charset encoding) {
6369
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/r2dbc/ConnectionFactoryBuilder.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import io.r2dbc.spi.ConnectionFactoryOptions;
3030
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
3131
import io.r2dbc.spi.ValidationDepth;
32-
import io.r2dbc.spi.Wrapped;
3332

3433
import org.springframework.boot.context.properties.PropertyMapper;
3534
import org.springframework.util.Assert;
@@ -104,14 +103,9 @@ public static ConnectionFactoryBuilder derivefrom(ConnectionFactory connectionFa
104103
}
105104

106105
private static ConnectionFactoryOptions extractOptionsIfPossible(ConnectionFactory connectionFactory) {
107-
if (connectionFactory instanceof OptionsCapableConnectionFactory) {
108-
return ((OptionsCapableConnectionFactory) connectionFactory).getOptions();
109-
}
110-
if (connectionFactory instanceof Wrapped) {
111-
Object unwrapped = ((Wrapped<?>) connectionFactory).unwrap();
112-
if (unwrapped instanceof ConnectionFactory) {
113-
return extractOptionsIfPossible((ConnectionFactory) unwrapped);
114-
}
106+
OptionsCapableConnectionFactory optionsCapable = OptionsCapableConnectionFactory.unwrapFrom(connectionFactory);
107+
if (optionsCapable != null) {
108+
return optionsCapable.getOptions();
115109
}
116110
return null;
117111
}

0 commit comments

Comments
 (0)