diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index 98457feda0c..b2a684af5c5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -30,7 +30,6 @@ import javax.sql.DataSource; 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; @@ -81,9 +80,9 @@ import org.springframework.util.StringUtils; * @author Dominic Gunn * @author Dan Zheng * @author András Deák + * @author Semyon Danilov * @since 1.1.0 */ -@SuppressWarnings("deprecation") @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Flyway.class) @Conditional(FlywayDataSourceCondition.class) @@ -113,7 +112,7 @@ public class FlywayAutoConfiguration { ResourceLoader resourceLoader, ObjectProvider dataSource, @FlywayDataSource ObjectProvider flywayDataSource, ObjectProvider fluentConfigurationCustomizers, - ObjectProvider callbacks, ObjectProvider flywayCallbacks) { + ObjectProvider callbacks) { FluentConfiguration configuration = new FluentConfiguration(resourceLoader.getClassLoader()); DataSource dataSourceToMigrate = configureDataSource(configuration, properties, dataSourceProperties, flywayDataSource.getIfAvailable(), dataSource.getIfAvailable()); @@ -122,10 +121,8 @@ public class FlywayAutoConfiguration { List orderedCallbacks = callbacks.orderedStream().collect(Collectors.toList()); configureCallbacks(configuration, orderedCallbacks); fluentConfigurationCustomizers.orderedStream().forEach((customizer) -> customizer.customize(configuration)); - Flyway flyway = configuration.load(); - List orderedFlywayCallbacks = flywayCallbacks.orderedStream().collect(Collectors.toList()); - configureFlywayCallbacks(flyway, orderedCallbacks, orderedFlywayCallbacks); - return flyway; + configureFlywayCallbacks(configuration, orderedCallbacks); + return configuration.load(); } private DataSource configureDataSource(FluentConfiguration configuration, FlywayProperties properties, @@ -168,6 +165,7 @@ public class FlywayAutoConfiguration { map.from(properties.getConnectRetries()).to(configuration::connectRetries); map.from(properties.getSchemas()).as(StringUtils::toStringArray).to(configuration::schemas); map.from(properties.getTable()).to(configuration::table); + map.from(properties.getTablespace()).to(configuration::tablespace); map.from(properties.getBaselineDescription()).to(configuration::baselineDescription); map.from(properties.getBaselineVersion()).to(configuration::baselineVersion); map.from(properties.getInstalledBy()).to(configuration::installedBy); @@ -200,6 +198,7 @@ public class FlywayAutoConfiguration { map.from(properties.getErrorOverrides()).whenNonNull().to(configuration::errorOverrides); map.from(properties.getLicenseKey()).whenNonNull().to(configuration::licenseKey); map.from(properties.getOracleSqlplus()).whenNonNull().to(configuration::oracleSqlplus); + map.from(properties.getOracleSqlplusWarn()).whenNonNull().to(configuration::oracleSqlplusWarn); map.from(properties.getStream()).whenNonNull().to(configuration::stream); map.from(properties.getUndoSqlMigrationPrefix()).whenNonNull().to(configuration::undoSqlMigrationPrefix); } @@ -210,14 +209,9 @@ public class FlywayAutoConfiguration { } } - private void configureFlywayCallbacks(Flyway flyway, List callbacks, - List flywayCallbacks) { - if (!flywayCallbacks.isEmpty()) { - if (!callbacks.isEmpty()) { - throw new IllegalStateException("Found a mixture of Callback and FlywayCallback beans." - + " One type must be used exclusively."); - } - flyway.setCallbacks(flywayCallbacks.toArray(new FlywayCallback[0])); + private void configureFlywayCallbacks(FluentConfiguration flyway, List callbacks) { + if (!callbacks.isEmpty()) { + flyway.callbacks(callbacks.toArray(new Callback[0])); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java index a4c6844e5ee..0ece0e045e5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java @@ -74,6 +74,13 @@ public class FlywayProperties { */ private String table = "flyway_schema_history"; + /** + * Tablespace in which the schema history table is created. Ignored when using a + * database that does not support tablespaces. Defaults to the default tablespace of + * the connection used by Flyway. + */ + private String tablespace; + /** * Description to tag an existing schema with when applying a baseline. */ @@ -252,6 +259,12 @@ public class FlywayProperties { */ private Boolean oracleSqlplus; + /** + * Whether to issue a warning rather than an error when a not-yet-supported Oracle + * SQL*Plus statement is encountered. Requires Flyway Pro or Flyway Enterprise. + */ + private Boolean oracleSqlplusWarn; + /** * Whether to stream SQL migrations when executing them. Requires Flyway Pro or Flyway * Enterprise. @@ -319,6 +332,14 @@ public class FlywayProperties { this.table = table; } + public String getTablespace() { + return this.tablespace; + } + + public void setTablespace(String tablespace) { + this.tablespace = tablespace; + } + public String getBaselineDescription() { return this.baselineDescription; } @@ -595,6 +616,14 @@ public class FlywayProperties { this.oracleSqlplus = oracleSqlplus; } + public Boolean getOracleSqlplusWarn() { + return this.oracleSqlplusWarn; + } + + public void setOracleSqlplusWarn(Boolean oracleSqlplusWarn) { + this.oracleSqlplusWarn = oracleSqlplusWarn; + } + public Boolean getStream() { return this.stream; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index b9053631e08..1a4e0cd4c42 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -16,7 +16,6 @@ package org.springframework.boot.autoconfigure.flyway; -import java.sql.Connection; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -30,7 +29,6 @@ import org.flywaydb.core.api.MigrationVersion; import org.flywaydb.core.api.callback.Callback; import org.flywaydb.core.api.callback.Context; import org.flywaydb.core.api.callback.Event; -import org.flywaydb.core.api.callback.FlywayCallback; import org.flywaydb.core.internal.license.FlywayProUpgradeRequiredException; import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.junit.jupiter.api.Test; @@ -72,7 +70,6 @@ import static org.mockito.Mockito.mock; * @author Dominic Gunn * @author András Deák */ -@SuppressWarnings("deprecation") class FlywayAutoConfigurationTests { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() @@ -89,7 +86,7 @@ class FlywayAutoConfigurationTests { this.contextRunner.withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:" + UUID.randomUUID()) .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - assertThat(context.getBean(Flyway.class).getDataSource()).isNotNull(); + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()).isNotNull(); }); } @@ -98,7 +95,7 @@ class FlywayAutoConfigurationTests { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) .withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:flywaytest").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - assertThat(context.getBean(Flyway.class).getDataSource()).isNotNull(); + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()).isNotNull(); }); } @@ -109,7 +106,7 @@ class FlywayAutoConfigurationTests { "spring.flyway.user:sa") .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - assertThat(context.getBean(Flyway.class).getDataSource()).isNotNull(); + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()).isNotNull(); }); } @@ -118,7 +115,7 @@ class FlywayAutoConfigurationTests { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) .withPropertyValues("spring.flyway.url:jdbc:hsqldb:mem:flywaytest").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - DataSource dataSource = context.getBean(Flyway.class).getDataSource(); + DataSource dataSource = context.getBean(Flyway.class).getConfiguration().getDataSource(); assertThat(dataSource).isNotNull(); assertThat(dataSource).hasFieldOrPropertyWithValue("user", "sa"); assertThat(dataSource).hasFieldOrPropertyWithValue("password", ""); @@ -130,7 +127,7 @@ class FlywayAutoConfigurationTests { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) .withPropertyValues("spring.flyway.user:sa").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - DataSource dataSource = context.getBean(Flyway.class).getDataSource(); + DataSource dataSource = context.getBean(Flyway.class).getConfiguration().getDataSource(); assertThat(dataSource).isNotNull(); assertThat(dataSource).extracting("url").asString().startsWith("jdbc:h2:mem:"); }); @@ -142,7 +139,7 @@ class FlywayAutoConfigurationTests { .withUserConfiguration(FlywayDataSourceConfiguration.class, EmbeddedDataSourceConfiguration.class) .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - assertThat(context.getBean(Flyway.class).getDataSource()) + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()) .isEqualTo(context.getBean("flywayDataSource")); }); } @@ -151,7 +148,8 @@ class FlywayAutoConfigurationTests { void flywayDataSourceWithoutDataSourceAutoConfiguration() { this.contextRunner.withUserConfiguration(FlywayDataSourceConfiguration.class).run((context) -> { assertThat(context).hasSingleBean(Flyway.class); - assertThat(context.getBean(Flyway.class).getDataSource()).isEqualTo(context.getBean("flywayDataSource")); + assertThat(context.getBean(Flyway.class).getConfiguration().getDataSource()) + .isEqualTo(context.getBean("flywayDataSource")); }); } @@ -175,7 +173,8 @@ class FlywayAutoConfigurationTests { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class).run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactly(new Location("classpath:db/migration")); + assertThat(flyway.getConfiguration().getLocations()) + .containsExactly(new Location("classpath:db/migration")); }); } @@ -186,8 +185,8 @@ class FlywayAutoConfigurationTests { .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactly(new Location("classpath:db/changelog"), - new Location("classpath:db/migration")); + assertThat(flyway.getConfiguration().getLocations()).containsExactly( + new Location("classpath:db/changelog"), new Location("classpath:db/migration")); }); } @@ -199,8 +198,8 @@ class FlywayAutoConfigurationTests { .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactly(new Location("classpath:db/changelog"), - new Location("classpath:db/migration")); + assertThat(flyway.getConfiguration().getLocations()).containsExactly( + new Location("classpath:db/changelog"), new Location("classpath:db/migration")); }); } @@ -210,7 +209,7 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.schemas:public").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(Arrays.asList(flyway.getSchemas()).toString()).isEqualTo("[public]"); + assertThat(Arrays.asList(flyway.getConfiguration().getSchemas()).toString()).isEqualTo("[public]"); }); } @@ -289,7 +288,8 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.baseline-version=0").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getBaselineVersion()).isEqualTo(MigrationVersion.fromVersion("0")); + assertThat(flyway.getConfiguration().getBaselineVersion()) + .isEqualTo(MigrationVersion.fromVersion("0")); }); } @@ -299,7 +299,8 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.baseline-version=1").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getBaselineVersion()).isEqualTo(MigrationVersion.fromVersion("1")); + assertThat(flyway.getConfiguration().getBaselineVersion()) + .isEqualTo(MigrationVersion.fromVersion("1")); }); } @@ -310,8 +311,8 @@ class FlywayAutoConfigurationTests { .run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactlyInAnyOrder(new Location("classpath:db/vendors/h2"), - new Location("classpath:db/changelog")); + assertThat(flyway.getConfiguration().getLocations()).containsExactlyInAnyOrder( + new Location("classpath:db/vendors/h2"), new Location("classpath:db/changelog")); }); } @@ -321,7 +322,8 @@ class FlywayAutoConfigurationTests { .withPropertyValues("spring.flyway.locations=classpath:db/vendors/{vendor}").run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactly(new Location("classpath:db/vendors/h2")); + assertThat(flyway.getConfiguration().getLocations()) + .containsExactly(new Location("classpath:db/vendors/h2")); }); } @@ -333,41 +335,14 @@ class FlywayAutoConfigurationTests { Flyway flyway = context.getBean(Flyway.class); Callback callbackOne = context.getBean("callbackOne", Callback.class); Callback callbackTwo = context.getBean("callbackTwo", Callback.class); - assertThat(flyway.getCallbacks()).hasSize(2); - assertThat(flyway.getCallbacks()).containsExactly(callbackTwo, callbackOne); + assertThat(flyway.getConfiguration().getCallbacks()).hasSize(2); + assertThat(flyway.getConfiguration().getCallbacks()).containsExactly(callbackTwo, callbackOne); InOrder orderedCallbacks = inOrder(callbackOne, callbackTwo); orderedCallbacks.verify(callbackTwo).handle(any(Event.class), any(Context.class)); orderedCallbacks.verify(callbackOne).handle(any(Event.class), any(Context.class)); }); } - @Test - void legacyCallbacksAreConfiguredAndOrdered() { - this.contextRunner - .withUserConfiguration(EmbeddedDataSourceConfiguration.class, LegacyCallbackConfiguration.class) - .run((context) -> { - assertThat(context).hasSingleBean(Flyway.class); - Flyway flyway = context.getBean(Flyway.class); - FlywayCallback callbackOne = context.getBean("legacyCallbackOne", FlywayCallback.class); - FlywayCallback callbackTwo = context.getBean("legacyCallbackTwo", FlywayCallback.class); - assertThat(flyway.getCallbacks()).hasSize(2); - InOrder orderedCallbacks = inOrder(callbackOne, callbackTwo); - orderedCallbacks.verify(callbackTwo).beforeMigrate(any(Connection.class)); - orderedCallbacks.verify(callbackOne).beforeMigrate(any(Connection.class)); - }); - } - - @Test - void callbacksAndLegacyCallbacksCannotBeMixed() { - this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, - LegacyCallbackConfiguration.class, CallbackConfiguration.class).run((context) -> { - assertThat(context).hasFailed(); - assertThat(context.getStartupFailure()) - .hasMessageContaining("Found a mixture of Callback and FlywayCallback beans." - + " One type must be used exclusively."); - }); - } - @Test void configurationCustomizersAreConfiguredAndOrdered() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class, @@ -435,6 +410,17 @@ class FlywayAutoConfigurationTests { }); } + @Test + void oracleSqlplusWarnIsCorrectlyMapped() { + this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) + .withPropertyValues("spring.flyway.oracle-sqlplus-warn=true").run((context) -> { + assertThat(context).hasFailed(); + Throwable failure = context.getStartupFailure(); + assertThat(failure).hasRootCauseInstanceOf(FlywayProUpgradeRequiredException.class); + assertThat(failure).hasMessageContaining(" oracle.sqlplusWarn "); + }); + } + @Test void streamIsCorrectlyMapped() { this.contextRunner.withUserConfiguration(EmbeddedDataSourceConfiguration.class) @@ -519,7 +505,7 @@ class FlywayAutoConfigurationTests { @Bean Flyway flyway() { - return new Flyway(); + return Flyway.configure().load(); } @Bean @@ -572,23 +558,6 @@ class FlywayAutoConfigurationTests { } - @Configuration(proxyBeanMethods = false) - static class LegacyCallbackConfiguration { - - @Bean - @Order(1) - FlywayCallback legacyCallbackOne() { - return mock(FlywayCallback.class); - } - - @Bean - @Order(0) - FlywayCallback legacyCallbackTwo() { - return mock(FlywayCallback.class); - } - - } - @Configuration(proxyBeanMethods = false) static class ConfigurationCustomizerConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java index 83cf4753b66..b200c6cf3f8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayPropertiesTests.java @@ -96,7 +96,7 @@ class FlywayPropertiesTests { ignoreProperties(properties, "url", "user", "password", "enabled", "checkLocation", "createDataSource"); // High level object we can't set with properties - ignoreProperties(configuration, "classLoader", "dataSource", "resolvers", "callbacks"); + ignoreProperties(configuration, "callbacks", "classLoader", "dataSource", "javaMigrations", "resolvers"); // Properties we don't want to expose ignoreProperties(configuration, "resolversAsClassNames", "callbacksAsClassNames"); // Handled by the conversion service @@ -107,8 +107,6 @@ class FlywayPropertiesTests { ignoreProperties(properties, "initSqls"); // Handled as dryRunOutput ignoreProperties(configuration, "dryRunOutputAsFile", "dryRunOutputAsFileName"); - // Deprecated - ignoreProperties(configuration, "errorHandlers", "errorHandlersAsClassNames"); List configurationKeys = new ArrayList<>(configuration.keySet()); Collections.sort(configurationKeys); List propertiesKeys = new ArrayList<>(properties.keySet()); diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index 286a7e32e08..92b22b64000 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -60,7 +60,7 @@ 2.10.6 3.8.0 2.2.0 - 5.2.4 + 6.0.1 2.3.28 6.8.2 3.0.2