From b6b37c91bb4e20f84f6ee347c98e0e1a6c0d98b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 3 Jul 2018 13:46:13 +0100 Subject: [PATCH] Upgrade to Flyway 5.1.3 Closes gh-13672 --- .../test/resources/db/migration/V1__init.sql | 1 + .../test/resources/db/migration/V1__init.sql | 1 + .../flyway/FlywayAutoConfiguration.java | 23 ++++- .../flyway/FlywayAutoConfigurationTests.java | 85 ++++++++++++++++--- .../test/resources/db/migration/V1__init.sql | 1 + .../test/resources/db/vendors/h2/V1__init.sql | 1 + .../spring-boot-dependencies/pom.xml | 2 +- .../src/main/asciidoc/howto.adoc | 7 +- 8 files changed, 104 insertions(+), 17 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/db/migration/V1__init.sql b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/db/migration/V1__init.sql index e69de29bb2..867c7c24f5 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/db/migration/V1__init.sql +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/resources/db/migration/V1__init.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS TEST; diff --git a/spring-boot-project/spring-boot-actuator/src/test/resources/db/migration/V1__init.sql b/spring-boot-project/spring-boot-actuator/src/test/resources/db/migration/V1__init.sql index e69de29bb2..867c7c24f5 100644 --- a/spring-boot-project/spring-boot-actuator/src/test/resources/db/migration/V1__init.sql +++ b/spring-boot-project/spring-boot-actuator/src/test/resources/db/migration/V1__init.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS TEST; 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 a93adbc15a..d593ccc4df 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 @@ -29,6 +29,7 @@ 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.springframework.beans.factory.ObjectProvider; @@ -71,6 +72,7 @@ import org.springframework.util.StringUtils; * @author Dominic Gunn * @since 1.1.0 */ +@SuppressWarnings("deprecation") @Configuration @ConditionalOnClass(Flyway.class) @ConditionalOnBean(DataSource.class) @@ -109,13 +111,16 @@ public class FlywayAutoConfiguration { private final FlywayMigrationStrategy migrationStrategy; - private List flywayCallbacks; + private final List callbacks; + + private final List flywayCallbacks; public FlywayConfiguration(FlywayProperties properties, DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader, ObjectProvider dataSource, @FlywayDataSource ObjectProvider flywayDataSource, ObjectProvider migrationStrategy, + ObjectProvider> callbacks, ObjectProvider> flywayCallbacks) { this.properties = properties; this.dataSourceProperties = dataSourceProperties; @@ -123,6 +128,7 @@ public class FlywayAutoConfiguration { this.dataSource = dataSource.getIfUnique(); this.flywayDataSource = flywayDataSource.getIfAvailable(); this.migrationStrategy = migrationStrategy.getIfAvailable(); + this.callbacks = callbacks.getIfAvailable(Collections::emptyList); this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList); } @@ -146,7 +152,20 @@ public class FlywayAutoConfiguration { else { flyway.setDataSource(this.dataSource); } - flyway.setCallbacks(this.flywayCallbacks.toArray(new FlywayCallback[0])); + 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."); + } + } String[] locations = new LocationResolver(flyway.getDataSource()) .resolveLocations(this.properties.getLocations()); checkLocationExists(locations); 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 90f2e8b864..5073819853 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 @@ -24,7 +24,11 @@ import java.util.Map; import javax.sql.DataSource; import org.flywaydb.core.Flyway; +import org.flywaydb.core.api.Location; 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.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.junit.Test; @@ -48,6 +52,7 @@ import org.springframework.stereotype.Component; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -62,6 +67,7 @@ import static org.mockito.Mockito.mock; * @author Stephane Nicoll * @author Dominic Gunn */ +@SuppressWarnings("deprecation") public class FlywayAutoConfigurationTests { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() @@ -137,7 +143,7 @@ public class FlywayAutoConfigurationTests { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); assertThat(flyway.getLocations()) - .containsExactly("classpath:db/migration"); + .containsExactly(new Location("classpath:db/migration")); }); } @@ -150,7 +156,8 @@ public class FlywayAutoConfigurationTests { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); assertThat(flyway.getLocations()).containsExactly( - "classpath:db/changelog", "classpath:db/migration"); + new Location("classpath:db/changelog"), + new Location("classpath:db/migration")); }); } @@ -163,7 +170,8 @@ public class FlywayAutoConfigurationTests { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); assertThat(flyway.getLocations()).containsExactly( - "classpath:db/changelog", "classpath:db/migration"); + new Location("classpath:db/changelog"), + new Location("classpath:db/migration")); }); } @@ -278,7 +286,8 @@ public class FlywayAutoConfigurationTests { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); assertThat(flyway.getLocations()).containsExactlyInAnyOrder( - "classpath:db/vendors/h2", "classpath:db/changelog"); + new Location("classpath:db/vendors/h2"), + new Location("classpath:db/changelog")); }); } @@ -291,7 +300,7 @@ public class FlywayAutoConfigurationTests { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); assertThat(flyway.getLocations()) - .containsExactly("classpath:db/vendors/h2"); + .containsExactly(new Location("classpath:db/vendors/h2")); }); } @@ -301,14 +310,31 @@ public class FlywayAutoConfigurationTests { CallbackConfiguration.class).run((context) -> { assertThat(context).hasSingleBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class); - FlywayCallback callbackOne = context.getBean("callbackOne", - FlywayCallback.class); - FlywayCallback callbackTwo = context.getBean("callbackTwo", - FlywayCallback.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); 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 + public 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) @@ -316,6 +342,19 @@ public class FlywayAutoConfigurationTests { }); } + @Test + public 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."); + }); + } + @Configuration protected static class FlywayDataSourceConfiguration { @@ -395,13 +434,37 @@ public class FlywayAutoConfigurationTests { @Bean @Order(1) - public FlywayCallback callbackOne() { + public Callback callbackOne() { + return mockCallback(); + } + + @Bean + @Order(0) + public Callback callbackTwo() { + return mockCallback(); + } + + private Callback mockCallback() { + Callback callback = mock(Callback.class); + given(callback.supports(any(Event.class), any(Context.class))) + .willReturn(true); + return callback; + } + + } + + @Configuration + static class LegacyCallbackConfiguration { + + @Bean + @Order(1) + public FlywayCallback legacyCallbackOne() { return mock(FlywayCallback.class); } @Bean @Order(0) - public FlywayCallback callbackTwo() { + public FlywayCallback legacyCallbackTwo() { return mock(FlywayCallback.class); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/migration/V1__init.sql b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/migration/V1__init.sql index e69de29bb2..867c7c24f5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/migration/V1__init.sql +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/migration/V1__init.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS TEST; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql index e69de29bb2..867c7c24f5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS TEST; diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml index 3152c662c9..22831bc372 100644 --- a/spring-boot-project/spring-boot-dependencies/pom.xml +++ b/spring-boot-project/spring-boot-dependencies/pom.xml @@ -58,7 +58,7 @@ 2.10.5 3.5.2 2.1.1 - 5.0.7 + 5.1.3 2.3.28 6.3.0 3.0.0 diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc index 4c05747823..c398792454 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -2225,9 +2225,10 @@ more control, provide a `@Bean` that implements Flyway supports SQL and Java https://flywaydb.org/documentation/callbacks.html[callbacks]. To use SQL-based callbacks, place the callback scripts in the `classpath:db/migration` folder. To use Java-based callbacks, create one or more beans that implement -`FlywayCallback` or, preferably, extend `BaseFlywayCallback`. Any such beans are -automatically registered with `Flyway`. They can be ordered by using `@Order` or by -implementing `Ordered`. +`Callback`. Any such beans are automatically registered with `Flyway`. They can be +ordered by using `@Order` or by implementing `Ordered`. Beans that implement the +deprecated `FlywayCallback` interface can also be detected, however they cannot be used +alongside `Callback` beans. By default, Flyway autowires the (`@Primary`) `DataSource` in your context and uses that for migrations. If you like to use a different `DataSource`, you can create