Use FluentConfiguration to configure Flyway

This commit stops binding the Flyway object directly to the environment
as mutating it will no longer be supported in Flyway 6.

This commit mirrors Flyway's configuration in FlywayProperties for the
most part.

Closes gh-14776
pull/14783/merge
Stephane Nicoll 6 years ago
parent 1d2f069580
commit e789bc0bb7

@ -32,6 +32,7 @@ import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.callback.FlywayCallback;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@ -46,9 +47,9 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.JdbcOperationsDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -61,6 +62,7 @@ import org.springframework.jdbc.support.MetaDataAccessException;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@ -76,7 +78,6 @@ import org.springframework.util.StringUtils;
* @author Dominic Gunn
* @since 1.1.0
*/
@SuppressWarnings("deprecation")
@Configuration
@ConditionalOnClass(Flyway.class)
@ConditionalOnBean(DataSource.class)
@ -97,6 +98,7 @@ public class FlywayAutoConfiguration {
return new FlywaySchemaManagementProvider(flyways);
}
@SuppressWarnings("deprecation")
@Configuration
@ConditionalOnMissingBean(Flyway.class)
@EnableConfigurationProperties({ DataSourceProperties.class, FlywayProperties.class })
@ -137,9 +139,18 @@ public class FlywayAutoConfiguration {
}
@Bean
@ConfigurationProperties(prefix = "spring.flyway")
public Flyway flyway() {
Flyway flyway = new SpringBootFlyway();
FluentConfiguration configuration = new FluentConfiguration();
DataSource dataSource = configureDataSource(configuration);
checkLocationExists(dataSource);
configureProperties(configuration);
configureCallbacks(configuration);
Flyway flyway = configuration.load();
configureFlywayCallbacks(flyway);
return flyway;
}
private DataSource configureDataSource(FluentConfiguration configuration) {
if (this.properties.isCreateDataSource()) {
String url = getProperty(this.properties::getUrl,
this.dataSourceProperties::getUrl);
@ -147,42 +158,25 @@ public class FlywayAutoConfiguration {
this.dataSourceProperties::getUsername);
String password = getProperty(this.properties::getPassword,
this.dataSourceProperties::getPassword);
flyway.setDataSource(url, user, password,
StringUtils.toStringArray(this.properties.getInitSqls()));
configuration.dataSource(url, user, password);
if (!CollectionUtils.isEmpty(this.properties.getInitSqls())) {
String initSql = StringUtils.collectionToDelimitedString(
this.properties.getInitSqls(), "\n");
configuration.initSql(initSql);
}
}
else if (this.flywayDataSource != null) {
flyway.setDataSource(this.flywayDataSource);
configuration.dataSource(this.flywayDataSource);
}
else {
flyway.setDataSource(this.dataSource);
}
if (!this.callbacks.isEmpty() || !this.flywayCallbacks.isEmpty()) {
if (this.flywayCallbacks.isEmpty()) {
flyway.setCallbacks(this.callbacks.toArray(new Callback[0]));
}
else if (this.callbacks.isEmpty()) {
flyway.setCallbacks(
this.flywayCallbacks.toArray(new FlywayCallback[0]));
}
else {
throw new IllegalStateException(
"Found a mixture of Callback and FlywayCallback beans."
+ " One type must be used exclusively.");
}
configuration.dataSource(this.dataSource);
}
checkLocationExists(flyway);
return flyway;
}
private String getProperty(Supplier<String> property,
Supplier<String> defaultValue) {
String value = property.get();
return (value != null) ? value : defaultValue.get();
return configuration.getDataSource();
}
private void checkLocationExists(Flyway flyway) {
private void checkLocationExists(DataSource dataSource) {
if (this.properties.isCheckLocation()) {
String[] locations = new LocationResolver(flyway.getDataSource())
String[] locations = new LocationResolver(dataSource)
.resolveLocations(this.properties.getLocations());
Assert.state(locations.length != 0,
"Migration script locations not configured");
@ -193,6 +187,86 @@ public class FlywayAutoConfiguration {
}
}
private void configureProperties(FluentConfiguration configuration) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
String[] locations = new LocationResolver(configuration.getDataSource())
.resolveLocations(this.properties.getLocations());
map.from(locations).to(configuration::locations);
map.from(this.properties.getEncoding()).to(configuration::encoding);
map.from(this.properties.getConnectRetries())
.to(configuration::connectRetries);
map.from(this.properties.getSchemas()).as(StringUtils::toStringArray)
.to(configuration::schemas);
map.from(this.properties.getTable()).to(configuration::table);
map.from(this.properties.getBaselineDescription())
.to(configuration::baselineDescription);
map.from(this.properties.getBaselineVersion())
.to(configuration::baselineVersion);
map.from(this.properties.getInstalledBy()).to(configuration::installedBy);
map.from(this.properties.getPlaceholders()).to(configuration::placeholders);
map.from(this.properties.getPlaceholderPrefix())
.to(configuration::placeholderPrefix);
map.from(this.properties.getPlaceholderSuffix())
.to(configuration::placeholderSuffix);
map.from(this.properties.isPlaceholderReplacement())
.to(configuration::placeholderReplacement);
map.from(this.properties.getSqlMigrationPrefix())
.to(configuration::sqlMigrationPrefix);
map.from(this.properties.getSqlMigrationSuffixes())
.as(StringUtils::toStringArray)
.to(configuration::sqlMigrationSuffixes);
map.from(this.properties.getSqlMigrationSeparator())
.to(configuration::sqlMigrationSeparator);
map.from(this.properties.getRepeatableSqlMigrationPrefix())
.to(configuration::repeatableSqlMigrationPrefix);
map.from(this.properties.getTarget()).to(configuration::target);
map.from(this.properties.isBaselineOnMigrate())
.to(configuration::baselineOnMigrate);
map.from(this.properties.isCleanDisabled()).to(configuration::cleanDisabled);
map.from(this.properties.isCleanOnValidationError())
.to(configuration::cleanOnValidationError);
map.from(this.properties.isGroup()).to(configuration::group);
map.from(this.properties.isIgnoreMissingMigrations())
.to(configuration::ignoreMissingMigrations);
map.from(this.properties.isIgnoreIgnoredMigrations())
.to(configuration::ignoreIgnoredMigrations);
map.from(this.properties.isIgnorePendingMigrations())
.to(configuration::ignorePendingMigrations);
map.from(this.properties.isIgnoreFutureMigrations())
.to(configuration::ignoreFutureMigrations);
map.from(this.properties.isMixed()).to(configuration::mixed);
map.from(this.properties.isOutOfOrder()).to(configuration::outOfOrder);
map.from(this.properties.isSkipDefaultCallbacks())
.to(configuration::skipDefaultCallbacks);
map.from(this.properties.isSkipDefaultResolvers())
.to(configuration::skipDefaultResolvers);
map.from(this.properties.isValidateOnMigrate())
.to(configuration::validateOnMigrate);
}
private void configureCallbacks(FluentConfiguration configuration) {
if (!this.callbacks.isEmpty()) {
configuration.callbacks(this.callbacks.toArray(new Callback[0]));
}
}
private void configureFlywayCallbacks(Flyway flyway) {
if (!this.flywayCallbacks.isEmpty()) {
if (!this.callbacks.isEmpty()) {
throw new IllegalStateException(
"Found a mixture of Callback and FlywayCallback beans."
+ " One type must be used exclusively.");
}
flyway.setCallbacks(this.flywayCallbacks.toArray(new FlywayCallback[0]));
}
}
private String getProperty(Supplier<String> property,
Supplier<String> defaultValue) {
String value = property.get();
return (value != null) ? value : defaultValue.get();
}
private boolean hasAtLeastOneLocation(String... locations) {
for (String location : locations) {
if (this.resourceLoader.getResource(normalizePrefix(location)).exists()) {
@ -278,16 +352,6 @@ public class FlywayAutoConfiguration {
}
private static class SpringBootFlyway extends Flyway {
@Override
public void setLocations(String... locations) {
super.setLocations(
new LocationResolver(getDataSource()).resolveLocations(locations));
}
}
private static class LocationResolver {
private static final String VENDOR_PLACEHOLDER = "{vendor}";

@ -16,33 +16,31 @@
package org.springframework.boot.autoconfigure.flyway;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.flywaydb.core.Flyway;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for Flyway database migrations. These are only the properties
* that Spring needs to validate and enable the migrations. If you want to control the
* location or format of the scripts you can use the same prefix ("flyway") to inject
* properties into the {@link Flyway} instance.
* Configuration properties for Flyway database migrations.
*
* @author Dave Syer
* @author Eddú Meléndez
* @author Stephane Nicoll
* @since 1.1.0
*/
@ConfigurationProperties(prefix = "spring.flyway", ignoreUnknownFields = true)
@ConfigurationProperties(prefix = "spring.flyway")
public class FlywayProperties {
/**
* The locations of migrations scripts. Can contain the special "{vendor}" placeholder
* to use vendor-specific locations.
* Whether to enable flyway.
*/
private List<String> locations = new ArrayList<>(
Collections.singletonList("classpath:db/migration"));
private boolean enabled = true;
/**
* Whether to check that migration scripts location exists.
@ -50,19 +48,92 @@ public class FlywayProperties {
private boolean checkLocation = true;
/**
* Whether to enable flyway.
* Locations of migrations scripts. Can contain the special "{vendor}" placeholder to
* use vendor-specific locations.
*/
private boolean enabled = true;
private List<String> locations = new ArrayList<>(
Collections.singletonList("classpath:db/migration"));
/**
* Login user of the database to migrate.
* Encoding of SQL migrations.
*/
private String user;
private Charset encoding = StandardCharsets.UTF_8;
/**
* JDBC password to use if you want Flyway to create its own DataSource.
* Maximum number of retries when attempting to connect to the database.
*/
private String password;
private int connectRetries;
/**
* Scheme names managed by Flyway (case-sensitive).
*/
private List<String> schemas = new ArrayList<>();
/**
* Name of the schema schema history table that will be used by Flyway.
*/
private String table = "flyway_schema_history";
/**
* Description to tag an existing schema with when applying a baseline.
*/
private String baselineDescription = "<< Flyway Baseline >>";
/**
* Version to tag an existing schema with when executing baseline.
*/
private String baselineVersion = "1";
/**
* Username recorded in the schema history table as having applied the migration.
*/
private String installedBy;
/**
* Placeholders and their replacements to apply to sql migration scripts.
*/
private Map<String, String> placeholders = new HashMap<>();
/**
* Prefix of placeholders in migration scripts.
*/
private String placeholderPrefix = "${";
/**
* Suffix of placeholders in migration scripts.
*/
private String placeholderSuffix = "}";
/**
* Perform placeholder replacement in migration scripts.
*/
private boolean placeholderReplacement = true;
/**
* File name prefix for SQL migrations.
*/
private String sqlMigrationPrefix = "V";
/**
* File name suffix for SQL migrations.
*/
private List<String> sqlMigrationSuffixes = new ArrayList<>(
Collections.singleton(".sql"));
/**
* File name separator for SQL migrations.
*/
private String sqlMigrationSeparator = "__";
/**
* File name prefix for repeatable SQL migrations.
*/
private String repeatableSqlMigrationPrefix = "R";
/**
* Target version up to which migrations should be considered.
*/
private String target;
/**
* JDBC url of the database to migrate. If not set, the primary configured data source
@ -70,50 +141,243 @@ public class FlywayProperties {
*/
private String url;
/**
* Login user of the database to migrate.
*/
private String user;
/**
* Login password of the database to migrate.
*/
private String password;
/**
* SQL statements to execute to initialize a connection immediately after obtaining
* it.
*/
private List<String> initSqls = new ArrayList<>();
public void setLocations(List<String> locations) {
this.locations = locations;
/**
* Whether to automatically call baseline when migrating a non-empty schema.
*/
private boolean baselineOnMigrate;
/**
* Whether to disable cleaning of the database.
*/
private boolean cleanDisabled;
/**
* Whether to automatically call clean when a validation error occurs.
*/
private boolean cleanOnValidationError;
/**
* Whether to group all pending migrations together in the same transaction when
* applying them.
*/
private boolean group;
/**
* Whether to ignore missing migrations when reading the schema history table.
*/
private boolean ignoreMissingMigrations;
/**
* Whether to ignore ignored migrations when reading the schema history table.
*/
private boolean ignoreIgnoredMigrations;
/**
* Whether to ignore pending migrations when reading the schema history table.
*/
private boolean ignorePendingMigrations;
/**
* Whether to ignore future migrations when reading the schema history table.
*/
private boolean ignoreFutureMigrations = true;
/**
* Whether to allow mixing transactional and non-transactional statements within the
* same migration.
*/
private boolean mixed;
/**
* Whether to allow migrations to be run out of order.
*/
private boolean outOfOrder;
/**
* Whether to skip default callbacks. If true, only custom callbacks are used.
*/
private boolean skipDefaultCallbacks;
/**
* Whether to skip default resolvers. If true, only custom resolvers are used.
*/
private boolean skipDefaultResolvers;
/**
* Whether to automatically call validate when performing a migration.
*/
private boolean validateOnMigrate = true;
public boolean isEnabled() {
return this.enabled;
}
public List<String> getLocations() {
return this.locations;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isCheckLocation() {
return this.checkLocation;
}
public void setCheckLocation(boolean checkLocation) {
this.checkLocation = checkLocation;
}
public boolean isCheckLocation() {
return this.checkLocation;
public List<String> getLocations() {
return this.locations;
}
public boolean isEnabled() {
return this.enabled;
public void setLocations(List<String> locations) {
this.locations = locations;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
public Charset getEncoding() {
return this.encoding;
}
public String getUser() {
return this.user;
public void setEncoding(Charset encoding) {
this.encoding = encoding;
}
public void setUser(String user) {
this.user = user;
public int getConnectRetries() {
return this.connectRetries;
}
public String getPassword() {
return (this.password != null) ? this.password : "";
public void setConnectRetries(int connectRetries) {
this.connectRetries = connectRetries;
}
public void setPassword(String password) {
this.password = password;
public List<String> getSchemas() {
return this.schemas;
}
public void setSchemas(List<String> schemas) {
this.schemas = schemas;
}
public String getTable() {
return this.table;
}
public void setTable(String table) {
this.table = table;
}
public String getBaselineDescription() {
return this.baselineDescription;
}
public void setBaselineDescription(String baselineDescription) {
this.baselineDescription = baselineDescription;
}
public String getBaselineVersion() {
return this.baselineVersion;
}
public void setBaselineVersion(String baselineVersion) {
this.baselineVersion = baselineVersion;
}
public String getInstalledBy() {
return this.installedBy;
}
public void setInstalledBy(String installedBy) {
this.installedBy = installedBy;
}
public Map<String, String> getPlaceholders() {
return this.placeholders;
}
public void setPlaceholders(Map<String, String> placeholders) {
this.placeholders = placeholders;
}
public String getPlaceholderPrefix() {
return this.placeholderPrefix;
}
public void setPlaceholderPrefix(String placeholderPrefix) {
this.placeholderPrefix = placeholderPrefix;
}
public String getPlaceholderSuffix() {
return this.placeholderSuffix;
}
public void setPlaceholderSuffix(String placeholderSuffix) {
this.placeholderSuffix = placeholderSuffix;
}
public boolean isPlaceholderReplacement() {
return this.placeholderReplacement;
}
public void setPlaceholderReplacement(boolean placeholderReplacement) {
this.placeholderReplacement = placeholderReplacement;
}
public String getSqlMigrationPrefix() {
return this.sqlMigrationPrefix;
}
public void setSqlMigrationPrefix(String sqlMigrationPrefix) {
this.sqlMigrationPrefix = sqlMigrationPrefix;
}
public List<String> getSqlMigrationSuffixes() {
return this.sqlMigrationSuffixes;
}
public void setSqlMigrationSuffixes(List<String> sqlMigrationSuffixes) {
this.sqlMigrationSuffixes = sqlMigrationSuffixes;
}
public String getSqlMigrationSeparator() {
return this.sqlMigrationSeparator;
}
public void setSqlMigrationSeparator(String sqlMigrationSeparator) {
this.sqlMigrationSeparator = sqlMigrationSeparator;
}
public String getRepeatableSqlMigrationPrefix() {
return this.repeatableSqlMigrationPrefix;
}
public void setRepeatableSqlMigrationPrefix(String repeatableSqlMigrationPrefix) {
this.repeatableSqlMigrationPrefix = repeatableSqlMigrationPrefix;
}
public String getTarget() {
return this.target;
}
public void setTarget(String target) {
this.target = target;
}
public boolean isCreateDataSource() {
return this.url != null || this.user != null;
}
public String getUrl() {
@ -124,6 +388,22 @@ public class FlywayProperties {
this.url = url;
}
public String getUser() {
return this.user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return (this.password != null) ? this.password : "";
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getInitSqls() {
return this.initSqls;
}
@ -132,8 +412,108 @@ public class FlywayProperties {
this.initSqls = initSqls;
}
public boolean isCreateDataSource() {
return this.url != null || this.user != null;
public boolean isBaselineOnMigrate() {
return this.baselineOnMigrate;
}
public void setBaselineOnMigrate(boolean baselineOnMigrate) {
this.baselineOnMigrate = baselineOnMigrate;
}
public boolean isCleanDisabled() {
return this.cleanDisabled;
}
public void setCleanDisabled(boolean cleanDisabled) {
this.cleanDisabled = cleanDisabled;
}
public boolean isCleanOnValidationError() {
return this.cleanOnValidationError;
}
public void setCleanOnValidationError(boolean cleanOnValidationError) {
this.cleanOnValidationError = cleanOnValidationError;
}
public boolean isGroup() {
return this.group;
}
public void setGroup(boolean group) {
this.group = group;
}
public boolean isIgnoreMissingMigrations() {
return this.ignoreMissingMigrations;
}
public void setIgnoreMissingMigrations(boolean ignoreMissingMigrations) {
this.ignoreMissingMigrations = ignoreMissingMigrations;
}
public boolean isIgnoreIgnoredMigrations() {
return this.ignoreIgnoredMigrations;
}
public void setIgnoreIgnoredMigrations(boolean ignoreIgnoredMigrations) {
this.ignoreIgnoredMigrations = ignoreIgnoredMigrations;
}
public boolean isIgnorePendingMigrations() {
return this.ignorePendingMigrations;
}
public void setIgnorePendingMigrations(boolean ignorePendingMigrations) {
this.ignorePendingMigrations = ignorePendingMigrations;
}
public boolean isIgnoreFutureMigrations() {
return this.ignoreFutureMigrations;
}
public void setIgnoreFutureMigrations(boolean ignoreFutureMigrations) {
this.ignoreFutureMigrations = ignoreFutureMigrations;
}
public boolean isMixed() {
return this.mixed;
}
public void setMixed(boolean mixed) {
this.mixed = mixed;
}
public boolean isOutOfOrder() {
return this.outOfOrder;
}
public void setOutOfOrder(boolean outOfOrder) {
this.outOfOrder = outOfOrder;
}
public boolean isSkipDefaultCallbacks() {
return this.skipDefaultCallbacks;
}
public void setSkipDefaultCallbacks(boolean skipDefaultCallbacks) {
this.skipDefaultCallbacks = skipDefaultCallbacks;
}
public boolean isSkipDefaultResolvers() {
return this.skipDefaultResolvers;
}
public void setSkipDefaultResolvers(boolean skipDefaultResolvers) {
this.skipDefaultResolvers = skipDefaultResolvers;
}
public boolean isValidateOnMigrate() {
return this.validateOnMigrate;
}
public void setValidateOnMigrate(boolean validateOnMigrate) {
this.validateOnMigrate = validateOnMigrate;
}
}

@ -1440,17 +1440,17 @@
},
{
"name": "spring.flyway.locations",
"type": "java.util.List<java.lang.String>",
"sourceType": "org.springframework.boot.autoconfigure.flyway.FlywayProperties",
"defaultValue": [
"classpath:db/migration"
]
},
{
"name": "spring.flyway.sql-migration-suffix",
"deprecation": {
"replacement": "spring.flyway.sql-migration-suffixes",
"level": "warning"
}
"name": "spring.flyway.sql-migration-suffixes",
"sourceType": "org.springframework.boot.autoconfigure.flyway.FlywayProperties",
"defaultValue": [
".sql"
]
},
{
"name": "spring.git.properties",

@ -0,0 +1,160 @@
/*
* Copyright 2012-2018 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.flyway;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.flywaydb.core.api.Location;
import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.configuration.ClassicConfiguration;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.api.configuration.FluentConfiguration;
import org.junit.Test;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link FlywayProperties}.
*
* @author Stephane Nicoll
*/
public class FlywayPropertiesTests {
@Test
public void defaultValuesAreConsistent() {
FlywayProperties properties = new FlywayProperties();
Configuration configuration = new FluentConfiguration();
assertThat(properties.getLocations().stream().map(Location::new)
.toArray(Location[]::new)).isEqualTo(configuration.getLocations());
assertThat(properties.getEncoding()).isEqualTo(configuration.getEncoding());
assertThat(properties.getConnectRetries())
.isEqualTo(configuration.getConnectRetries());
assertThat(properties.getSchemas())
.isEqualTo(Arrays.asList(configuration.getSchemas()));
assertThat(properties.getTable()).isEqualTo(configuration.getTable());
assertThat(properties.getBaselineDescription())
.isEqualTo(configuration.getBaselineDescription());
assertThat(MigrationVersion.fromVersion(properties.getBaselineVersion()))
.isEqualTo(configuration.getBaselineVersion());
assertThat(properties.getInstalledBy()).isEqualTo(configuration.getInstalledBy());
assertThat(properties.getPlaceholders())
.isEqualTo(configuration.getPlaceholders());
assertThat(properties.getPlaceholderPrefix())
.isEqualToIgnoringWhitespace(configuration.getPlaceholderPrefix());
assertThat(properties.getPlaceholderSuffix())
.isEqualTo(configuration.getPlaceholderSuffix());
assertThat(properties.isPlaceholderReplacement())
.isEqualTo(configuration.isPlaceholderReplacement());
assertThat(properties.getSqlMigrationPrefix())
.isEqualTo(configuration.getSqlMigrationPrefix());
assertThat(properties.getSqlMigrationSuffixes())
.isEqualTo(Arrays.asList(configuration.getSqlMigrationSuffixes()));
assertThat(properties.getSqlMigrationSeparator())
.isEqualTo(properties.getSqlMigrationSeparator());
assertThat(properties.getRepeatableSqlMigrationPrefix())
.isEqualTo(properties.getRepeatableSqlMigrationPrefix());
assertThat(properties.getTarget()).isNull();
assertThat(configuration.getTarget()).isNull();
assertThat(configuration.getInitSql()).isNull();
assertThat(properties.getInitSqls()).isEmpty();
assertThat(configuration.isBaselineOnMigrate())
.isEqualTo(properties.isBaselineOnMigrate());
assertThat(configuration.isCleanDisabled())
.isEqualTo(properties.isCleanDisabled());
assertThat(configuration.isCleanOnValidationError())
.isEqualTo(properties.isCleanOnValidationError());
assertThat(configuration.isGroup()).isEqualTo(properties.isGroup());
assertThat(configuration.isIgnoreMissingMigrations())
.isEqualTo(properties.isIgnoreMissingMigrations());
assertThat(configuration.isIgnoreIgnoredMigrations())
.isEqualTo(properties.isIgnoreIgnoredMigrations());
assertThat(configuration.isIgnorePendingMigrations())
.isEqualTo(properties.isIgnorePendingMigrations());
assertThat(configuration.isIgnoreFutureMigrations())
.isEqualTo(properties.isIgnoreFutureMigrations());
assertThat(configuration.isMixed()).isEqualTo(properties.isMixed());
assertThat(configuration.isOutOfOrder()).isEqualTo(properties.isOutOfOrder());
assertThat(configuration.isSkipDefaultCallbacks())
.isEqualTo(properties.isSkipDefaultCallbacks());
assertThat(configuration.isSkipDefaultResolvers())
.isEqualTo(properties.isSkipDefaultResolvers());
assertThat(configuration.isValidateOnMigrate())
.isEqualTo(properties.isValidateOnMigrate());
}
@Test
public void expectedPropertiesAreManaged() {
Map<String, PropertyDescriptor> properties = indexProperties(
PropertyAccessorFactory.forBeanPropertyAccess(new FlywayProperties()));
Map<String, PropertyDescriptor> configuration = indexProperties(
PropertyAccessorFactory
.forBeanPropertyAccess(new ClassicConfiguration()));
// Properties specific settings
ignoreProperties(properties, "url", "user", "password", "enabled",
"checkLocation", "createDataSource");
// High level object we can't set with properties
ignoreProperties(configuration, "classLoader", "dataSource", "resolvers",
"callbacks");
// Properties we don't want to expose
ignoreProperties(configuration, "resolversAsClassNames", "callbacksAsClassNames");
// Handled by the conversion service
ignoreProperties(configuration, "baselineVersionAsString", "encodingAsString",
"locationsAsStrings", "targetAsString");
// Handled as initSql array
ignoreProperties(configuration, "initSql");
ignoreProperties(properties, "initSqls");
// Pro version only
ignoreProperties(configuration, "batch", "dryRunOutput", "dryRunOutputAsFile",
"dryRunOutputAsFileName", "errorHandlers", "errorHandlersAsClassNames",
"errorOverrides", "licenseKey", "oracleSqlplus", "stream",
"undoSqlMigrationPrefix");
List<String> configurationKeys = new ArrayList(configuration.keySet());
Collections.sort(configurationKeys);
List<String> propertiesKeys = new ArrayList(properties.keySet());
Collections.sort(propertiesKeys);
assertThat(configurationKeys).isEqualTo(propertiesKeys);
}
private void ignoreProperties(Map<String, ?> index, String... propertyNames) {
for (String propertyName : propertyNames) {
assertThat(index.remove(propertyName))
.describedAs("Property to ignore should be present " + propertyName)
.isNotNull();
}
}
private Map<String, PropertyDescriptor> indexProperties(BeanWrapper beanWrapper) {
Map<String, PropertyDescriptor> descriptor = new HashMap<>();
for (PropertyDescriptor propertyDescriptor : beanWrapper
.getPropertyDescriptors()) {
descriptor.put(propertyDescriptor.getName(), propertyDescriptor);
}
ignoreProperties(descriptor, "class");
return descriptor;
}
}

@ -552,47 +552,42 @@ content into your application. Rather, pick only the properties that you need.
# ----------------------------------------
# FLYWAY ({sc-spring-boot-autoconfigure}/flyway/FlywayProperties.{sc-ext}[FlywayProperties])
spring.flyway.baseline-description= #
spring.flyway.baseline-on-migrate= #
spring.flyway.baseline-version=1 # Version to start migration
spring.flyway.batch= #
spring.flyway.baseline-description=<< Flyway Baseline >> # Description to tag an existing schema with when applying a baseline.
spring.flyway.baseline-on-migrate=false # Whether to automatically call baseline when migrating a non-empty schema.
spring.flyway.baseline-version=1 # Version to tag an existing schema with when executing baseline.
spring.flyway.check-location=true # Whether to check that migration scripts location exists.
spring.flyway.clean-disabled= #
spring.flyway.clean-on-validation-error= #
spring.flyway.dry-run-output= #
spring.flyway.clean-disabled=false # Whether to disable cleaning of the database.
spring.flyway.clean-on-validation-error=false # Whether to automatically call clean when a validation error occurs.
spring.flyway.connect-retries=0 # Maximum number of retries when attempting to connect to the database.
spring.flyway.enabled=true # Whether to enable flyway.
spring.flyway.error-handlers= #
spring.flyway.error-overrides= #
spring.flyway.group= #
spring.flyway.ignore-ignored-migrations= #
spring.flyway.ignore-future-migrations= #
spring.flyway.ignore-missing-migrations= #
spring.flyway.encoding=UTF-8 # Encoding of SQL migrations.
spring.flyway.group=false # Whether to group all pending migrations together in the same transaction when applying them.
spring.flyway.ignore-future-migrations=true # Whether to ignore future migrations when reading the schema history table.
spring.flyway.ignore-ignored-migrations=false # Whether to ignore ignored migrations when reading the schema history table.
spring.flyway.ignore-missing-migrations=false # Whether to ignore missing migrations when reading the schema history table.
spring.flyway.ignore-pending-migrations=false # Whether to ignore pending migrations when reading the schema history table.
spring.flyway.init-sqls= # SQL statements to execute to initialize a connection immediately after obtaining it.
spring.flyway.installed-by= #
spring.flyway.locations=classpath:db/migration # The locations of migrations scripts.
spring.flyway.mixed= #
spring.flyway.oracle-sqlplus= #
spring.flyway.out-of-order= #
spring.flyway.password= # JDBC password to use if you want Flyway to create its own DataSource.
spring.flyway.placeholder-prefix= #
spring.flyway.placeholder-replacement= #
spring.flyway.placeholder-suffix= #
spring.flyway.placeholders.*= #
spring.flyway.repeatable-sql-migration-prefix= #
spring.flyway.schemas= # Schemas to update.
spring.flyway.skip-default-callbacks= #
spring.flyway.skip-default-resolvers= #
spring.flyway.sql-migration-prefix=V #
spring.flyway.sql-migration-separator= #
spring.flyway.sql-migration-suffix=.sql #
spring.flyway.sql-migration-suffixes= #
spring.flyway.stream= #
spring.flyway.table= #
spring.flyway.target= #
spring.flyway.undo-sql-migration-prefix= #
spring.flyway.installed-by= # Username recorded in the schema history table as having applied the migration.
spring.flyway.locations=classpath:db/migration # Locations of migrations scripts. Can contain the special "{vendor}" placeholder to use vendor-specific locations.
spring.flyway.mixed=false # Whether to allow mixing transactional and non-transactional statements within the same migration.
spring.flyway.out-of-order=false # Whether to allow migrations to be run out of order.
spring.flyway.password= # Login password of the database to migrate.
spring.flyway.placeholder-prefix=${ # Prefix of placeholders in migration scripts.
spring.flyway.placeholder-replacement=true # Perform placeholder replacement in migration scripts.
spring.flyway.placeholder-suffix=} # Suffix of placeholders in migration scripts.
spring.flyway.placeholders= # Placeholders and their replacements to apply to sql migration scripts.
spring.flyway.repeatable-sql-migration-prefix=R # File name prefix for repeatable SQL migrations.
spring.flyway.schemas= # Scheme names managed by Flyway (case-sensitive).
spring.flyway.skip-default-callbacks=false # Whether to skip default callbacks. If true, only custom callbacks are used.
spring.flyway.skip-default-resolvers=false # Whether to skip default resolvers. If true, only custom resolvers are used.
spring.flyway.sql-migration-prefix=V # File name prefix for SQL migrations.
spring.flyway.sql-migration-separator=__ # File name separator for SQL migrations.
spring.flyway.sql-migration-suffixes=.sql # File name suffix for SQL migrations.
spring.flyway.table=flyway_schema_history # Name of the schema schema history table that will be used by Flyway.
spring.flyway.target= # Target version up to which migrations should be considered.
spring.flyway.url= # JDBC url of the database to migrate. If not set, the primary configured data source is used.
spring.flyway.user= # Login user of the database to migrate.
spring.flyway.validate-on-migrate= #
spring.flyway.validate-on-migrate=true # Whether to automatically call validate when performing a migration.
# LIQUIBASE ({sc-spring-boot-autoconfigure}/liquibase/LiquibaseProperties.{sc-ext}[LiquibaseProperties])
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-master.yaml # Change log configuration path.

@ -2327,11 +2327,11 @@ according to the type of the database (such as `db/migration/mysql` for MySQL).
of supported databases is available in
{sc-spring-boot}/jdbc/DatabaseDriver.{sc-ext}[`DatabaseDriver`].
See the Flyway class from flyway-core for details of available settings such as schemas
and others. In addition, Spring Boot provides a small set of properties (in
{sc-spring-boot-autoconfigure}/flyway/FlywayProperties.{sc-ext}[`FlywayProperties`])
that can be used to disable the migrations or switch off the location checking. Spring
Boot calls `Flyway.migrate()` to perform the database migration. If you would like
{sc-spring-boot-autoconfigure}/flyway/FlywayProperties.{sc-ext}[`FlywayProperties`]
provides most of Flyway's settings and a small set of additional properties that can be
used to disable the migrations or switch off the location checking.
Spring Boot calls `Flyway.migrate()` to perform the database migration. If you would like
more control, provide a `@Bean` that implements
{sc-spring-boot-autoconfigure}/flyway/FlywayMigrationStrategy.{sc-ext}[`FlywayMigrationStrategy`].

Loading…
Cancel
Save