Reinstate mode for controlling DB initialization
Closes gh-26682
This commit is contained in:
parent
1a0e008a8c
commit
c52143727a
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.jdbc;
|
package org.springframework.boot.autoconfigure.jdbc;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
@ -40,15 +39,14 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfi
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.DataSourceInitializationCondition;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.DataSourceInitializationCondition;
|
||||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||||
import org.springframework.boot.jdbc.DataSourceInitializationMode;
|
import org.springframework.boot.jdbc.DataSourceInitializationMode;
|
||||||
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
|
|
||||||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
||||||
|
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.ConditionContext;
|
import org.springframework.context.annotation.ConditionContext;
|
||||||
import org.springframework.context.annotation.DependsOn;
|
import org.springframework.context.annotation.DependsOn;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.core.io.Resource;
|
|
||||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||||
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
|
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
@ -80,6 +78,19 @@ class DataSourceInitializationConfiguration {
|
||||||
return fallbackLocations;
|
return fallbackLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DatabaseInitializationMode mapMode(DataSourceInitializationMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case ALWAYS:
|
||||||
|
return DatabaseInitializationMode.ALWAYS;
|
||||||
|
case EMBEDDED:
|
||||||
|
return DatabaseInitializationMode.EMBEDDED;
|
||||||
|
case NEVER:
|
||||||
|
return DatabaseInitializationMode.NEVER;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unexpected initialization mode '" + mode + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fully-qualified to work around javac bug in JDK 1.8
|
// Fully-qualified to work around javac bug in JDK 1.8
|
||||||
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
|
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
|
||||||
@org.springframework.context.annotation.Conditional(DifferentCredentialsCondition.class)
|
@org.springframework.context.annotation.Conditional(DifferentCredentialsCondition.class)
|
||||||
|
|
@ -96,10 +107,10 @@ class DataSourceInitializationConfiguration {
|
||||||
settings.setContinueOnError(properties.isContinueOnError());
|
settings.setContinueOnError(properties.isContinueOnError());
|
||||||
settings.setSeparator(properties.getSeparator());
|
settings.setSeparator(properties.getSeparator());
|
||||||
settings.setEncoding(properties.getSqlScriptEncoding());
|
settings.setEncoding(properties.getSqlScriptEncoding());
|
||||||
|
settings.setMode(mapMode(properties.getInitializationMode()));
|
||||||
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
|
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
|
||||||
properties.getSchemaUsername(), properties.getSchemaPassword());
|
properties.getSchemaUsername(), properties.getSchemaPassword());
|
||||||
return new InitializationModeDataSourceScriptDatabaseInitializer(initializationDataSource, settings,
|
return new DataSourceScriptDatabaseInitializer(initializationDataSource, settings);
|
||||||
properties.getInitializationMode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
@ -111,10 +122,10 @@ class DataSourceInitializationConfiguration {
|
||||||
settings.setContinueOnError(properties.isContinueOnError());
|
settings.setContinueOnError(properties.isContinueOnError());
|
||||||
settings.setSeparator(properties.getSeparator());
|
settings.setSeparator(properties.getSeparator());
|
||||||
settings.setEncoding(properties.getSqlScriptEncoding());
|
settings.setEncoding(properties.getSqlScriptEncoding());
|
||||||
|
settings.setMode(mapMode(properties.getInitializationMode()));
|
||||||
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
|
DataSource initializationDataSource = determineDataSource(dataSource::getObject,
|
||||||
properties.getDataUsername(), properties.getDataPassword());
|
properties.getDataUsername(), properties.getDataPassword());
|
||||||
return new InitializationModeDataSourceScriptDatabaseInitializer(initializationDataSource, settings,
|
return new DataSourceScriptDatabaseInitializer(initializationDataSource, settings);
|
||||||
properties.getInitializationMode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DifferentCredentialsCondition extends AnyNestedCondition {
|
static class DifferentCredentialsCondition extends AnyNestedCondition {
|
||||||
|
|
@ -154,8 +165,8 @@ class DataSourceInitializationConfiguration {
|
||||||
settings.setContinueOnError(properties.isContinueOnError());
|
settings.setContinueOnError(properties.isContinueOnError());
|
||||||
settings.setSeparator(properties.getSeparator());
|
settings.setSeparator(properties.getSeparator());
|
||||||
settings.setEncoding(properties.getSqlScriptEncoding());
|
settings.setEncoding(properties.getSqlScriptEncoding());
|
||||||
return new InitializationModeDataSourceScriptDatabaseInitializer(dataSource, settings,
|
settings.setMode(mapMode(properties.getInitializationMode()));
|
||||||
properties.getInitializationMode());
|
return new DataSourceScriptDatabaseInitializer(dataSource, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DataSourceInitializationCondition extends SpringBootCondition {
|
static class DataSourceInitializationCondition extends SpringBootCondition {
|
||||||
|
|
@ -186,25 +197,4 @@ class DataSourceInitializationConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class InitializationModeDataSourceScriptDatabaseInitializer extends DataSourceScriptDatabaseInitializer {
|
|
||||||
|
|
||||||
private final DataSourceInitializationMode mode;
|
|
||||||
|
|
||||||
InitializationModeDataSourceScriptDatabaseInitializer(DataSource dataSource,
|
|
||||||
DatabaseInitializationSettings settings, DataSourceInitializationMode mode) {
|
|
||||||
super(dataSource, settings);
|
|
||||||
this.mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator,
|
|
||||||
Charset encoding) {
|
|
||||||
if (this.mode == DataSourceInitializationMode.ALWAYS || (this.mode == DataSourceInitializationMode.EMBEDDED
|
|
||||||
&& EmbeddedDatabaseConnection.isEmbedded(getDataSource()))) {
|
|
||||||
super.runScripts(resources, continueOnError, separator, encoding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,7 @@ public class DataSourceProperties implements BeanClassLoaderAware, InitializingB
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@DeprecatedConfigurationProperty(replacement = "spring.sql.init.enabled")
|
@DeprecatedConfigurationProperty(replacement = "spring.sql.init.mode")
|
||||||
public DataSourceInitializationMode getInitializationMode() {
|
public DataSourceInitializationMode getInitializationMode() {
|
||||||
return this.initializationMode;
|
return this.initializationMode;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ final class SettingsCreator {
|
||||||
settings.setContinueOnError(properties.isContinueOnError());
|
settings.setContinueOnError(properties.isContinueOnError());
|
||||||
settings.setSeparator(properties.getSeparator());
|
settings.setSeparator(properties.getSeparator());
|
||||||
settings.setEncoding(properties.getEncoding());
|
settings.setEncoding(properties.getEncoding());
|
||||||
|
settings.setMode(properties.getMode());
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,14 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration.SqlInitializationModeCondition;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
import org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer;
|
||||||
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
|
@ -36,11 +39,25 @@ import org.springframework.context.annotation.Import;
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)
|
@ConditionalOnMissingBean(AbstractScriptDatabaseInitializer.class)
|
||||||
@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
|
|
||||||
@AutoConfigureAfter({ R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class })
|
@AutoConfigureAfter({ R2dbcAutoConfiguration.class, DataSourceAutoConfiguration.class })
|
||||||
@EnableConfigurationProperties(SqlInitializationProperties.class)
|
@EnableConfigurationProperties(SqlInitializationProperties.class)
|
||||||
@Import({ DatabaseInitializationDependencyConfigurer.class, R2dbcInitializationConfiguration.class,
|
@Import({ DatabaseInitializationDependencyConfigurer.class, R2dbcInitializationConfiguration.class,
|
||||||
DataSourceInitializationConfiguration.class })
|
DataSourceInitializationConfiguration.class })
|
||||||
|
@ConditionalOnProperty(prefix = "spring.sql.init", name = "enabled", matchIfMissing = true)
|
||||||
|
@Conditional(SqlInitializationModeCondition.class)
|
||||||
public class SqlInitializationAutoConfiguration {
|
public class SqlInitializationAutoConfiguration {
|
||||||
|
|
||||||
|
static class SqlInitializationModeCondition extends NoneNestedConditions {
|
||||||
|
|
||||||
|
SqlInitializationModeCondition() {
|
||||||
|
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnProperty(prefix = "spring.sql.init", name = "mode", havingValue = "never")
|
||||||
|
static class ModeIsNever {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import java.nio.charset.Charset;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.sql.init.DatabaseInitializationMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ConfigurationProperties Configuration properties} for initializing an SQL
|
* {@link ConfigurationProperties Configuration properties} for initializing an SQL
|
||||||
|
|
@ -74,6 +75,11 @@ public class SqlInitializationProperties {
|
||||||
*/
|
*/
|
||||||
private Charset encoding;
|
private Charset encoding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mode to apply when determining whether initialization should be performed.
|
||||||
|
*/
|
||||||
|
private DatabaseInitializationMode mode = DatabaseInitializationMode.EMBEDDED;
|
||||||
|
|
||||||
public List<String> getSchemaLocations() {
|
public List<String> getSchemaLocations() {
|
||||||
return this.schemaLocations;
|
return this.schemaLocations;
|
||||||
}
|
}
|
||||||
|
|
@ -138,4 +144,12 @@ public class SqlInitializationProperties {
|
||||||
this.encoding = encoding;
|
this.encoding = encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DatabaseInitializationMode getMode() {
|
||||||
|
return this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMode(DatabaseInitializationMode mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1746,7 +1746,11 @@
|
||||||
"name": "spring.sql.init.enabled",
|
"name": "spring.sql.init.enabled",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
"description": "Whether basic script-based initialization of an SQL database is enabled.",
|
"description": "Whether basic script-based initialization of an SQL database is enabled.",
|
||||||
"defaultValue": true
|
"defaultValue": true,
|
||||||
|
"deprecation": {
|
||||||
|
"replacement": "spring.sql.init.mode",
|
||||||
|
"level": "error"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "spring.thymeleaf.prefix",
|
"name": "spring.thymeleaf.prefix",
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ class HikariDriverConfigurationFailureAnalyzerTests {
|
||||||
private BeanCreationException createFailure(Class<?> configuration) {
|
private BeanCreationException createFailure(Class<?> configuration) {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
TestPropertyValues.of("spring.datasource.type=" + HikariDataSource.class.getName(),
|
TestPropertyValues.of("spring.datasource.type=" + HikariDataSource.class.getName(),
|
||||||
"spring.datasource.hikari.data-source-class-name=com.example.Foo").applyTo(context);
|
"spring.datasource.hikari.data-source-class-name=com.example.Foo", "spring.sql.init.mode=always")
|
||||||
|
.applyTo(context);
|
||||||
context.register(configuration);
|
context.register(configuration);
|
||||||
try {
|
try {
|
||||||
context.refresh();
|
context.refresh();
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFlywaySwitchOffDdlAuto() {
|
void testFlywaySwitchOffDdlAuto() {
|
||||||
contextRunner().withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city")
|
contextRunner().withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city")
|
||||||
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
|
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
|
||||||
.run((context) -> assertThat(context).hasNotFailed());
|
.run((context) -> assertThat(context).hasNotFailed());
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +167,7 @@ class HibernateJpaAutoConfigurationTests extends AbstractJpaAutoConfigurationTes
|
||||||
@Test
|
@Test
|
||||||
void testFlywayPlusValidation() {
|
void testFlywayPlusValidation() {
|
||||||
contextRunner()
|
contextRunner()
|
||||||
.withPropertyValues("spring.sql.init.enabled:false", "spring.flyway.locations:classpath:db/city",
|
.withPropertyValues("spring.sql.init.mode:never", "spring.flyway.locations:classpath:db/city",
|
||||||
"spring.jpa.hibernate.ddl-auto:validate")
|
"spring.jpa.hibernate.ddl-auto:validate")
|
||||||
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
|
.withConfiguration(AutoConfigurations.of(FlywayAutoConfiguration.class))
|
||||||
.run((context) -> assertThat(context).hasNotFailed());
|
.run((context) -> assertThat(context).hasNotFailed());
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,10 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
|
||||||
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
|
||||||
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
import org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer;
|
||||||
|
import org.springframework.boot.logging.LogLevel;
|
||||||
import org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializer;
|
import org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
|
|
@ -64,12 +66,21 @@ public class SqlInitializationAutoConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Deprecated
|
||||||
void whenConnectionFactoryIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
|
void whenConnectionFactoryIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
|
||||||
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
|
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
|
||||||
.withPropertyValues("spring.sql.init.enabled:false")
|
.withPropertyValues("spring.sql.init.enabled:false")
|
||||||
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenConnectionFactoryIsAvailableAndModeIsNeverThenInitializerIsNotAutoConfigured() {
|
||||||
|
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
|
||||||
|
.withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
|
||||||
|
.withPropertyValues("spring.sql.init.mode:never")
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenDataSourceIsAvailableThenDataSourceInitializerIsAutoConfigured() {
|
void whenDataSourceIsAvailableThenDataSourceInitializerIsAutoConfigured() {
|
||||||
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||||
|
|
@ -77,12 +88,20 @@ public class SqlInitializationAutoConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Deprecated
|
||||||
void whenDataSourceIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
|
void whenDataSourceIsAvailableAndInitializationIsDisabledThenInitializerIsNotAutoConfigured() {
|
||||||
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||||
.withPropertyValues("spring.sql.init.enabled:false")
|
.withPropertyValues("spring.sql.init.enabled:false")
|
||||||
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenDataSourceIsAvailableAndModeIsNeverThenThenInitializerIsNotAutoConfigured() {
|
||||||
|
this.contextRunner.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
|
||||||
|
.withPropertyValues("spring.sql.init.mode:never")
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(AbstractScriptDatabaseInitializer.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenDataSourceAndConnectionFactoryAreAvailableThenOnlyR2dbcInitializerIsAutoConfigured() {
|
void whenDataSourceAndConnectionFactoryAreAvailableThenOnlyR2dbcInitializerIsAutoConfigured() {
|
||||||
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
|
this.contextRunner.withConfiguration(AutoConfigurations.of(R2dbcAutoConfiguration.class))
|
||||||
|
|
@ -135,6 +154,11 @@ public class SqlInitializationAutoConfigurationTests {
|
||||||
// No-op
|
// No-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEmbeddedDatabase() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,9 @@ It loads SQL from the standard root classpath locations: `schema.sql` and `data.
|
||||||
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[].
|
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.
|
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).
|
For example, you might choose to set it to the vendor name of the database (`hsqldb`, `h2`, `oracle`, `mysql`, `postgresql`, and so on).
|
||||||
SQL database initialization can be disabled by setting configprop:spring.sql.init.enabled[] to `false`.
|
By default, SQL database initialization is only performed when using an embedded in-memory database.
|
||||||
|
To always initialize an SQL database, irrespective of its type, set configprop:spring.sql.init.mode[] to `always`.
|
||||||
|
Similarly, to disable initialization, set configprop:spring.sql.init.mode[] to `never`.
|
||||||
By default, Spring Boot enables the fail-fast feature of its script-based database initializer.
|
By default, Spring Boot enables the fail-fast feature of its script-based database initializer.
|
||||||
This means that, if the scripts cause exceptions, the application fails to start.
|
This means that, if the scripts cause exceptions, the application fails to start.
|
||||||
You can tune that behavior by setting configprop:spring.sql.init.continue-on-error[].
|
You can tune that behavior by setting configprop:spring.sql.init.continue-on-error[].
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
@ -58,6 +59,11 @@ public class DataSourceScriptDatabaseInitializer extends AbstractScriptDatabaseI
|
||||||
return this.dataSource;
|
return this.dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEmbeddedDatabase() {
|
||||||
|
return EmbeddedDatabaseConnection.isEmbedded(this.dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator, Charset encoding) {
|
protected void runScripts(List<Resource> resources, boolean continueOnError, String separator, Charset encoding) {
|
||||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import io.r2dbc.spi.ConnectionFactory;
|
||||||
import io.r2dbc.spi.ConnectionFactoryOptions;
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
import io.r2dbc.spi.ConnectionFactoryOptions.Builder;
|
||||||
import io.r2dbc.spi.ValidationDepth;
|
import io.r2dbc.spi.ValidationDepth;
|
||||||
import io.r2dbc.spi.Wrapped;
|
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.PropertyMapper;
|
import org.springframework.boot.context.properties.PropertyMapper;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
@ -104,14 +103,9 @@ public final class ConnectionFactoryBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectionFactoryOptions extractOptionsIfPossible(ConnectionFactory connectionFactory) {
|
private static ConnectionFactoryOptions extractOptionsIfPossible(ConnectionFactory connectionFactory) {
|
||||||
if (connectionFactory instanceof OptionsCapableConnectionFactory) {
|
OptionsCapableConnectionFactory optionsCapable = OptionsCapableConnectionFactory.unwrapFrom(connectionFactory);
|
||||||
return ((OptionsCapableConnectionFactory) connectionFactory).getOptions();
|
if (optionsCapable != null) {
|
||||||
}
|
return optionsCapable.getOptions();
|
||||||
if (connectionFactory instanceof Wrapped) {
|
|
||||||
Object unwrapped = ((Wrapped<?>) connectionFactory).unwrap();
|
|
||||||
if (unwrapped instanceof ConnectionFactory) {
|
|
||||||
return extractOptionsIfPossible((ConnectionFactory) unwrapped);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
|
|
||||||
package org.springframework.boot.r2dbc;
|
package org.springframework.boot.r2dbc;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
import io.r2dbc.spi.ConnectionFactoryOptions;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
|
@ -31,21 +36,25 @@ public enum EmbeddedDatabaseConnection {
|
||||||
/**
|
/**
|
||||||
* No Connection.
|
* No Connection.
|
||||||
*/
|
*/
|
||||||
NONE(null, null),
|
NONE(null, null, (options) -> false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* H2 Database Connection.
|
* H2 Database Connection.
|
||||||
*/
|
*/
|
||||||
H2("io.r2dbc.h2.H2ConnectionFactoryProvider",
|
H2("io.r2dbc.h2.H2ConnectionFactoryProvider", "r2dbc:h2:mem:///%s?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE",
|
||||||
"r2dbc:h2:mem:///%s?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
|
(options) -> options.getValue(ConnectionFactoryOptions.DRIVER).equals("h2")
|
||||||
|
&& options.getValue(ConnectionFactoryOptions.PROTOCOL).equals("mem"));
|
||||||
|
|
||||||
private final String driverClassName;
|
private final String driverClassName;
|
||||||
|
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
EmbeddedDatabaseConnection(String driverClassName, String url) {
|
private Predicate<ConnectionFactoryOptions> embedded;
|
||||||
|
|
||||||
|
EmbeddedDatabaseConnection(String driverClassName, String url, Predicate<ConnectionFactoryOptions> embedded) {
|
||||||
this.driverClassName = driverClassName;
|
this.driverClassName = driverClassName;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
this.embedded = embedded;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -81,4 +90,27 @@ public enum EmbeddedDatabaseConnection {
|
||||||
return NONE;
|
return NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to determine if a given connection factory represents an
|
||||||
|
* embedded database type.
|
||||||
|
* @param connectionFactory the connection factory to interrogate
|
||||||
|
* @return true if the connection factory represents an embedded database
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public static boolean isEmbedded(ConnectionFactory connectionFactory) {
|
||||||
|
OptionsCapableConnectionFactory optionsCapable = OptionsCapableConnectionFactory.unwrapFrom(connectionFactory);
|
||||||
|
if (optionsCapable == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot determine database's type as ConnectionFactory is not options-capable");
|
||||||
|
}
|
||||||
|
ConnectionFactoryOptions options = optionsCapable.getOptions();
|
||||||
|
for (EmbeddedDatabaseConnection candidate : values()) {
|
||||||
|
if (candidate.embedded.test(options)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,4 +67,28 @@ public class OptionsCapableConnectionFactory implements Wrapped<ConnectionFactor
|
||||||
return this.delegate;
|
return this.delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns, if possible, an {@code OptionsCapableConnectionFactory} by unwrapping the
|
||||||
|
* given {@code connectionFactory} as necessary. If the given
|
||||||
|
* {@code connectionFactory} does not wrap an {@code OptionsCapableConnectionFactory}
|
||||||
|
* and is not itself an {@code OptionsCapableConnectionFactory}, {@code null} is
|
||||||
|
* returned.
|
||||||
|
* @param connectionFactory the connection factory to unwrap
|
||||||
|
* @return the {@code OptionsCapableConnectionFactory} or {@code null}
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public static OptionsCapableConnectionFactory unwrapFrom(ConnectionFactory connectionFactory) {
|
||||||
|
if (connectionFactory instanceof OptionsCapableConnectionFactory) {
|
||||||
|
return (OptionsCapableConnectionFactory) connectionFactory;
|
||||||
|
}
|
||||||
|
if (connectionFactory instanceof Wrapped) {
|
||||||
|
Object unwrapped = ((Wrapped<?>) connectionFactory).unwrap();
|
||||||
|
if (unwrapped instanceof ConnectionFactory) {
|
||||||
|
return unwrapFrom((ConnectionFactory) unwrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.List;
|
||||||
import io.r2dbc.spi.ConnectionFactory;
|
import io.r2dbc.spi.ConnectionFactory;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.boot.r2dbc.EmbeddedDatabaseConnection;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
|
|
@ -51,6 +52,11 @@ public class R2dbcScriptDatabaseInitializer extends AbstractScriptDatabaseInitia
|
||||||
this.connectionFactory = connectionFactory;
|
this.connectionFactory = connectionFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEmbeddedDatabase() {
|
||||||
|
return EmbeddedDatabaseConnection.isEmbedded(this.connectionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void runScripts(List<Resource> scripts, boolean continueOnError, String separator, Charset encoding) {
|
protected void runScripts(List<Resource> scripts, boolean continueOnError, String separator, Charset encoding) {
|
||||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,30 @@ public abstract class AbstractScriptDatabaseInitializer implements ResourceLoade
|
||||||
* {@code false}
|
* {@code false}
|
||||||
*/
|
*/
|
||||||
public boolean initializeDatabase() {
|
public boolean initializeDatabase() {
|
||||||
ScriptLocationResolver locationResolver = new ScriptLocationResolver(this.resourceLoader);
|
if (isEnabled()) {
|
||||||
boolean initialized = applySchemaScripts(locationResolver);
|
ScriptLocationResolver locationResolver = new ScriptLocationResolver(this.resourceLoader);
|
||||||
initialized = applyDataScripts(locationResolver) || initialized;
|
boolean initialized = applySchemaScripts(locationResolver);
|
||||||
return initialized;
|
initialized = applyDataScripts(locationResolver) || initialized;
|
||||||
|
return initialized;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEnabled() {
|
||||||
|
if (this.settings.getMode() == DatabaseInitializationMode.NEVER) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.settings.getMode() == DatabaseInitializationMode.ALWAYS || isEmbeddedDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the database that is to be initialized is embedded.
|
||||||
|
* @return {@code true} if the database is embedded, otherwise {@code false}
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
protected boolean isEmbeddedDatabase() {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Database initialization mode is '" + this.settings.getMode() + "' and database type is unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean applySchemaScripts(ScriptLocationResolver locationResolver) {
|
private boolean applySchemaScripts(ScriptLocationResolver locationResolver) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.sql.init;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported database initialization modes.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
* @since 2.5.1
|
||||||
|
* @see AbstractScriptDatabaseInitializer
|
||||||
|
*/
|
||||||
|
public enum DatabaseInitializationMode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always initialize the database.
|
||||||
|
*/
|
||||||
|
ALWAYS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only initialize an embedded database.
|
||||||
|
*/
|
||||||
|
EMBEDDED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Never initialize the database.
|
||||||
|
*/
|
||||||
|
NEVER
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -37,6 +37,8 @@ public class DatabaseInitializationSettings {
|
||||||
|
|
||||||
private Charset encoding;
|
private Charset encoding;
|
||||||
|
|
||||||
|
private DatabaseInitializationMode mode = DatabaseInitializationMode.EMBEDDED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the locations of the schema (DDL) scripts to apply to the database.
|
* Returns the locations of the schema (DDL) scripts to apply to the database.
|
||||||
* @return the locations of the schema scripts
|
* @return the locations of the schema scripts
|
||||||
|
|
@ -123,4 +125,24 @@ public class DatabaseInitializationSettings {
|
||||||
this.encoding = encoding;
|
this.encoding = encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mode to use when determining whether database initialization should be
|
||||||
|
* performed.
|
||||||
|
* @return the initialization mode
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public DatabaseInitializationMode getMode() {
|
||||||
|
return this.mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the mode the use when determining whether database initialization should be
|
||||||
|
* performed.
|
||||||
|
* @param mode the initialization mode
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public void setMode(DatabaseInitializationMode mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package org.springframework.boot.jdbc.init;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
import com.zaxxer.hikari.HikariDataSource;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
|
||||||
|
|
@ -25,6 +27,7 @@ import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializerTests;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializerTests;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
|
import org.springframework.boot.testsupport.BuildOutput;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,22 +37,44 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
*/
|
*/
|
||||||
class DataSourceScriptDatabaseInitializerTests extends AbstractScriptDatabaseInitializerTests {
|
class DataSourceScriptDatabaseInitializerTests extends AbstractScriptDatabaseInitializerTests {
|
||||||
|
|
||||||
private final HikariDataSource dataSource = DataSourceBuilder.create().type(HikariDataSource.class)
|
private final HikariDataSource embeddedDataSource = DataSourceBuilder.create().type(HikariDataSource.class)
|
||||||
.url("jdbc:h2:mem:" + UUID.randomUUID()).build();
|
.url("jdbc:h2:mem:" + UUID.randomUUID()).build();
|
||||||
|
|
||||||
|
private final HikariDataSource standloneDataSource = DataSourceBuilder.create().type(HikariDataSource.class)
|
||||||
|
.url("jdbc:h2:file:" + new BuildOutput(DataSourceScriptDatabaseInitializerTests.class).getRootLocation()
|
||||||
|
.getAbsolutePath() + "/" + UUID.randomUUID())
|
||||||
|
.build();
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void closeDataSource() {
|
void closeDataSource() {
|
||||||
this.dataSource.close();
|
this.embeddedDataSource.close();
|
||||||
|
this.standloneDataSource.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractScriptDatabaseInitializer createInitializer(DatabaseInitializationSettings settings) {
|
protected AbstractScriptDatabaseInitializer createEmbeddedDatabaseInitializer(
|
||||||
return new DataSourceScriptDatabaseInitializer(this.dataSource, settings);
|
DatabaseInitializationSettings settings) {
|
||||||
|
return new DataSourceScriptDatabaseInitializer(this.embeddedDataSource, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int numberOfRows(String sql) {
|
protected AbstractScriptDatabaseInitializer createStandaloneDatabaseInitializer(
|
||||||
return new JdbcTemplate(this.dataSource).queryForObject(sql, Integer.class);
|
DatabaseInitializationSettings settings) {
|
||||||
|
return new DataSourceScriptDatabaseInitializer(this.standloneDataSource, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int numberOfEmbeddedRows(String sql) {
|
||||||
|
return numberOfRows(this.embeddedDataSource, sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int numberOfStandaloneRows(String sql) {
|
||||||
|
return numberOfRows(this.standloneDataSource, sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int numberOfRows(DataSource dataSource, String sql) {
|
||||||
|
return new JdbcTemplate(dataSource).queryForObject(sql, Integer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,23 @@ package org.springframework.boot.r2dbc;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import io.r2dbc.spi.ConnectionFactories;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link EmbeddedDatabaseConnection}.
|
* Tests for {@link EmbeddedDatabaseConnection}.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author Andy Wilkinson
|
||||||
*/
|
*/
|
||||||
class EmbeddedDatabaseConnectionTests {
|
class EmbeddedDatabaseConnectionTests {
|
||||||
|
|
||||||
|
|
@ -53,6 +57,41 @@ class EmbeddedDatabaseConnectionTests {
|
||||||
.isEqualTo(EmbeddedDatabaseConnection.NONE);
|
.isEqualTo(EmbeddedDatabaseConnection.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenH2IsInMemoryThenIsEmbeddedReturnsTrue() {
|
||||||
|
assertThat(EmbeddedDatabaseConnection
|
||||||
|
.isEmbedded(ConnectionFactoryBuilder.withUrl("r2dbc:h2:mem:///" + UUID.randomUUID()).build())).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenH2IsUsingFileStorageThenIsEmbeddedReturnsFalse() {
|
||||||
|
assertThat(EmbeddedDatabaseConnection
|
||||||
|
.isEmbedded(ConnectionFactoryBuilder.withUrl("r2dbc:h2:file:///" + UUID.randomUUID()).build()))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenPoolIsBasedByH2InMemoryThenIsEmbeddedReturnsTrue() {
|
||||||
|
assertThat(EmbeddedDatabaseConnection
|
||||||
|
.isEmbedded(ConnectionFactoryBuilder.withUrl("r2dbc:pool:h2:mem:///" + UUID.randomUUID()).build()))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenPoolIsBasedByH2WithFileStorageThenIsEmbeddedReturnsFalse() {
|
||||||
|
assertThat(EmbeddedDatabaseConnection
|
||||||
|
.isEmbedded(ConnectionFactoryBuilder.withUrl("r2dbc:pool:h2:file:///" + UUID.randomUUID()).build()))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenConnectionFactoryIsNotOptionsCapableThenIsEmbeddedThrows() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> EmbeddedDatabaseConnection
|
||||||
|
.isEmbedded(ConnectionFactories.get("r2dbc:pool:h2:mem:///" + UUID.randomUUID())))
|
||||||
|
.withMessage("Cannot determine database's type as ConnectionFactory is not options-capable");
|
||||||
|
}
|
||||||
|
|
||||||
static Stream<Arguments> urlParameters() {
|
static Stream<Arguments> urlParameters() {
|
||||||
return Stream.of(Arguments.arguments(EmbeddedDatabaseConnection.NONE, null),
|
return Stream.of(Arguments.arguments(EmbeddedDatabaseConnection.NONE, null),
|
||||||
Arguments.arguments(EmbeddedDatabaseConnection.H2,
|
Arguments.arguments(EmbeddedDatabaseConnection.H2,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import org.springframework.boot.r2dbc.ConnectionFactoryBuilder;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
|
||||||
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializerTests;
|
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializerTests;
|
||||||
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
|
||||||
|
import org.springframework.boot.testsupport.BuildOutput;
|
||||||
import org.springframework.r2dbc.core.DatabaseClient;
|
import org.springframework.r2dbc.core.DatabaseClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,18 +34,38 @@ import org.springframework.r2dbc.core.DatabaseClient;
|
||||||
*/
|
*/
|
||||||
class R2dbcScriptDatabaseInitializerTests extends AbstractScriptDatabaseInitializerTests {
|
class R2dbcScriptDatabaseInitializerTests extends AbstractScriptDatabaseInitializerTests {
|
||||||
|
|
||||||
private final ConnectionFactory connectionFactory = ConnectionFactoryBuilder
|
private final ConnectionFactory embeddedConnectionFactory = ConnectionFactoryBuilder
|
||||||
.withUrl("r2dbc:h2:mem:///" + UUID.randomUUID() + "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
|
.withUrl("r2dbc:h2:mem:///" + UUID.randomUUID() + "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
private final ConnectionFactory standaloneConnectionFactory = ConnectionFactoryBuilder.withUrl("r2dbc:h2:file:///"
|
||||||
|
+ new BuildOutput(R2dbcScriptDatabaseInitializerTests.class).getRootLocation().getAbsolutePath() + "/"
|
||||||
|
+ UUID.randomUUID() + "?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE").build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AbstractScriptDatabaseInitializer createInitializer(DatabaseInitializationSettings settings) {
|
protected AbstractScriptDatabaseInitializer createEmbeddedDatabaseInitializer(
|
||||||
return new R2dbcScriptDatabaseInitializer(this.connectionFactory, settings);
|
DatabaseInitializationSettings settings) {
|
||||||
|
return new R2dbcScriptDatabaseInitializer(this.embeddedConnectionFactory, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected int numberOfRows(String sql) {
|
protected AbstractScriptDatabaseInitializer createStandaloneDatabaseInitializer(
|
||||||
return DatabaseClient.create(this.connectionFactory).sql(sql).map((row, metadata) -> row.get(0)).first()
|
DatabaseInitializationSettings settings) {
|
||||||
|
return new R2dbcScriptDatabaseInitializer(this.standaloneConnectionFactory, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int numberOfEmbeddedRows(String sql) {
|
||||||
|
return numberOfRows(this.embeddedConnectionFactory, sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int numberOfStandaloneRows(String sql) {
|
||||||
|
return numberOfRows(this.standaloneConnectionFactory, sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int numberOfRows(ConnectionFactory connectionFactory, String sql) {
|
||||||
|
return DatabaseClient.create(connectionFactory).sql(sql).map((row, metadata) -> row.get(0)).first()
|
||||||
.map((number) -> ((Number) number).intValue()).block();
|
.map((number) -> ((Number) number).intValue()).block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,16 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
settings.setDataLocations(Arrays.asList("data.sql"));
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThat(initializer.initializeDatabase()).isTrue();
|
assertThat(initializer.initializeDatabase()).isTrue();
|
||||||
assertThat(numberOfRows("SELECT COUNT(*) FROM EXAMPLE")).isEqualTo(1);
|
assertThat(numberOfEmbeddedRows("SELECT COUNT(*) FROM EXAMPLE")).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void whenContinueOnErrorIsFalseThenInitializationFailsOnError() {
|
void whenContinueOnErrorIsFalseThenInitializationFailsOnError() {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setDataLocations(Arrays.asList("data.sql"));
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThatExceptionOfType(DataAccessException.class).isThrownBy(() -> initializer.initializeDatabase());
|
assertThatExceptionOfType(DataAccessException.class).isThrownBy(() -> initializer.initializeDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,7 +56,7 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setContinueOnError(true);
|
settings.setContinueOnError(true);
|
||||||
settings.setDataLocations(Arrays.asList("data.sql"));
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThat(initializer.initializeDatabase()).isTrue();
|
assertThat(initializer.initializeDatabase()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,7 +64,7 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
void whenNoScriptsExistAtASchemaLocationThenInitializationFails() {
|
void whenNoScriptsExistAtASchemaLocationThenInitializationFails() {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setSchemaLocations(Arrays.asList("does-not-exist.sql"));
|
settings.setSchemaLocations(Arrays.asList("does-not-exist.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThatIllegalStateException().isThrownBy(initializer::initializeDatabase)
|
assertThatIllegalStateException().isThrownBy(initializer::initializeDatabase)
|
||||||
.withMessage("No schema scripts found at location 'does-not-exist.sql'");
|
.withMessage("No schema scripts found at location 'does-not-exist.sql'");
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
void whenNoScriptsExistAtADataLocationThenInitializationFails() {
|
void whenNoScriptsExistAtADataLocationThenInitializationFails() {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setDataLocations(Arrays.asList("does-not-exist.sql"));
|
settings.setDataLocations(Arrays.asList("does-not-exist.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThatIllegalStateException().isThrownBy(initializer::initializeDatabase)
|
assertThatIllegalStateException().isThrownBy(initializer::initializeDatabase)
|
||||||
.withMessage("No data scripts found at location 'does-not-exist.sql'");
|
.withMessage("No data scripts found at location 'does-not-exist.sql'");
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
void whenNoScriptsExistAtAnOptionalSchemaLocationThenInitializationSucceeds() {
|
void whenNoScriptsExistAtAnOptionalSchemaLocationThenInitializationSucceeds() {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setSchemaLocations(Arrays.asList("optional:does-not-exist.sql"));
|
settings.setSchemaLocations(Arrays.asList("optional:does-not-exist.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThat(initializer.initializeDatabase()).isFalse();
|
assertThat(initializer.initializeDatabase()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,12 +90,81 @@ public abstract class AbstractScriptDatabaseInitializerTests {
|
||||||
void whenNoScriptsExistAtAnOptionalDataLocationThenInitializationSucceeds() {
|
void whenNoScriptsExistAtAnOptionalDataLocationThenInitializationSucceeds() {
|
||||||
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
settings.setDataLocations(Arrays.asList("optional:does-not-exist.sql"));
|
settings.setDataLocations(Arrays.asList("optional:does-not-exist.sql"));
|
||||||
AbstractScriptDatabaseInitializer initializer = createInitializer(settings);
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
assertThat(initializer.initializeDatabase()).isFalse();
|
assertThat(initializer.initializeDatabase()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract AbstractScriptDatabaseInitializer createInitializer(DatabaseInitializationSettings settings);
|
@Test
|
||||||
|
void whenModeIsNeverThenEmbeddedDatabaseIsNotInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.NEVER);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract int numberOfRows(String sql);
|
@Test
|
||||||
|
void whenModeIsNeverThenStandaloneDatabaseIsNotInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.NEVER);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createStandaloneDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenModeIsEmbeddedThenEmbeddedDatabaseIsInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.EMBEDDED);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isTrue();
|
||||||
|
assertThat(numberOfEmbeddedRows("SELECT COUNT(*) FROM EXAMPLE")).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenModeIsEmbeddedThenStandaloneDatabaseIsNotInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.EMBEDDED);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createStandaloneDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenModeIsAlwaysThenEmbeddedDatabaseIsInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.ALWAYS);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createEmbeddedDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isTrue();
|
||||||
|
assertThat(numberOfEmbeddedRows("SELECT COUNT(*) FROM EXAMPLE")).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenModeIsAlwaysThenStandaloneDatabaseIsInitialized() {
|
||||||
|
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
|
||||||
|
settings.setSchemaLocations(Arrays.asList("schema.sql"));
|
||||||
|
settings.setDataLocations(Arrays.asList("data.sql"));
|
||||||
|
settings.setMode(DatabaseInitializationMode.ALWAYS);
|
||||||
|
AbstractScriptDatabaseInitializer initializer = createStandaloneDatabaseInitializer(settings);
|
||||||
|
assertThat(initializer.initializeDatabase()).isTrue();
|
||||||
|
assertThat(numberOfStandaloneRows("SELECT COUNT(*) FROM EXAMPLE")).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractScriptDatabaseInitializer createStandaloneDatabaseInitializer(
|
||||||
|
DatabaseInitializationSettings settings);
|
||||||
|
|
||||||
|
protected abstract AbstractScriptDatabaseInitializer createEmbeddedDatabaseInitializer(
|
||||||
|
DatabaseInitializationSettings settings);
|
||||||
|
|
||||||
|
protected abstract int numberOfEmbeddedRows(String sql);
|
||||||
|
|
||||||
|
protected abstract int numberOfStandaloneRows(String sql);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue