Commit b6b37c91 authored by Andy Wilkinson's avatar Andy Wilkinson

Upgrade to Flyway 5.1.3

Closes gh-13672
parent 23480474
...@@ -29,6 +29,7 @@ import javax.sql.DataSource; ...@@ -29,6 +29,7 @@ import javax.sql.DataSource;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationVersion; 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.callback.FlywayCallback;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
...@@ -71,6 +72,7 @@ import org.springframework.util.StringUtils; ...@@ -71,6 +72,7 @@ import org.springframework.util.StringUtils;
* @author Dominic Gunn * @author Dominic Gunn
* @since 1.1.0 * @since 1.1.0
*/ */
@SuppressWarnings("deprecation")
@Configuration @Configuration
@ConditionalOnClass(Flyway.class) @ConditionalOnClass(Flyway.class)
@ConditionalOnBean(DataSource.class) @ConditionalOnBean(DataSource.class)
...@@ -109,13 +111,16 @@ public class FlywayAutoConfiguration { ...@@ -109,13 +111,16 @@ public class FlywayAutoConfiguration {
private final FlywayMigrationStrategy migrationStrategy; private final FlywayMigrationStrategy migrationStrategy;
private List<FlywayCallback> flywayCallbacks; private final List<Callback> callbacks;
private final List<FlywayCallback> flywayCallbacks;
public FlywayConfiguration(FlywayProperties properties, public FlywayConfiguration(FlywayProperties properties,
DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader, DataSourceProperties dataSourceProperties, ResourceLoader resourceLoader,
ObjectProvider<DataSource> dataSource, ObjectProvider<DataSource> dataSource,
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource, @FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy, ObjectProvider<FlywayMigrationStrategy> migrationStrategy,
ObjectProvider<List<Callback>> callbacks,
ObjectProvider<List<FlywayCallback>> flywayCallbacks) { ObjectProvider<List<FlywayCallback>> flywayCallbacks) {
this.properties = properties; this.properties = properties;
this.dataSourceProperties = dataSourceProperties; this.dataSourceProperties = dataSourceProperties;
...@@ -123,6 +128,7 @@ public class FlywayAutoConfiguration { ...@@ -123,6 +128,7 @@ public class FlywayAutoConfiguration {
this.dataSource = dataSource.getIfUnique(); this.dataSource = dataSource.getIfUnique();
this.flywayDataSource = flywayDataSource.getIfAvailable(); this.flywayDataSource = flywayDataSource.getIfAvailable();
this.migrationStrategy = migrationStrategy.getIfAvailable(); this.migrationStrategy = migrationStrategy.getIfAvailable();
this.callbacks = callbacks.getIfAvailable(Collections::emptyList);
this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList); this.flywayCallbacks = flywayCallbacks.getIfAvailable(Collections::emptyList);
} }
...@@ -146,7 +152,20 @@ public class FlywayAutoConfiguration { ...@@ -146,7 +152,20 @@ public class FlywayAutoConfiguration {
else { else {
flyway.setDataSource(this.dataSource); 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()) String[] locations = new LocationResolver(flyway.getDataSource())
.resolveLocations(this.properties.getLocations()); .resolveLocations(this.properties.getLocations());
checkLocationExists(locations); checkLocationExists(locations);
......
...@@ -24,7 +24,11 @@ import java.util.Map; ...@@ -24,7 +24,11 @@ import java.util.Map;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.Location;
import org.flywaydb.core.api.MigrationVersion; 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.api.callback.FlywayCallback;
import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform; import org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform;
import org.junit.Test; import org.junit.Test;
...@@ -48,6 +52,7 @@ import org.springframework.stereotype.Component; ...@@ -48,6 +52,7 @@ import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
...@@ -62,6 +67,7 @@ import static org.mockito.Mockito.mock; ...@@ -62,6 +67,7 @@ import static org.mockito.Mockito.mock;
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Dominic Gunn * @author Dominic Gunn
*/ */
@SuppressWarnings("deprecation")
public class FlywayAutoConfigurationTests { public class FlywayAutoConfigurationTests {
private ApplicationContextRunner contextRunner = new ApplicationContextRunner() private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
...@@ -137,7 +143,7 @@ public class FlywayAutoConfigurationTests { ...@@ -137,7 +143,7 @@ public class FlywayAutoConfigurationTests {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
assertThat(flyway.getLocations()) assertThat(flyway.getLocations())
.containsExactly("classpath:db/migration"); .containsExactly(new Location("classpath:db/migration"));
}); });
} }
...@@ -150,7 +156,8 @@ public class FlywayAutoConfigurationTests { ...@@ -150,7 +156,8 @@ public class FlywayAutoConfigurationTests {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
assertThat(flyway.getLocations()).containsExactly( 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 { ...@@ -163,7 +170,8 @@ public class FlywayAutoConfigurationTests {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
assertThat(flyway.getLocations()).containsExactly( 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 { ...@@ -278,7 +286,8 @@ public class FlywayAutoConfigurationTests {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
assertThat(flyway.getLocations()).containsExactlyInAnyOrder( 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 { ...@@ -291,7 +300,7 @@ public class FlywayAutoConfigurationTests {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
assertThat(flyway.getLocations()) assertThat(flyway.getLocations())
.containsExactly("classpath:db/vendors/h2"); .containsExactly(new Location("classpath:db/vendors/h2"));
}); });
} }
...@@ -301,14 +310,31 @@ public class FlywayAutoConfigurationTests { ...@@ -301,14 +310,31 @@ public class FlywayAutoConfigurationTests {
CallbackConfiguration.class).run((context) -> { CallbackConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(Flyway.class); assertThat(context).hasSingleBean(Flyway.class);
Flyway flyway = context.getBean(Flyway.class); Flyway flyway = context.getBean(Flyway.class);
FlywayCallback callbackOne = context.getBean("callbackOne", Callback callbackOne = context.getBean("callbackOne", Callback.class);
FlywayCallback.class); Callback callbackTwo = context.getBean("callbackTwo", Callback.class);
FlywayCallback callbackTwo = context.getBean("callbackTwo",
FlywayCallback.class);
assertThat(flyway.getCallbacks()).hasSize(2); assertThat(flyway.getCallbacks()).hasSize(2);
assertThat(flyway.getCallbacks()).containsExactly(callbackTwo, assertThat(flyway.getCallbacks()).containsExactly(callbackTwo,
callbackOne); callbackOne);
InOrder orderedCallbacks = inOrder(callbackOne, callbackTwo); 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) orderedCallbacks.verify(callbackTwo)
.beforeMigrate(any(Connection.class)); .beforeMigrate(any(Connection.class));
orderedCallbacks.verify(callbackOne) orderedCallbacks.verify(callbackOne)
...@@ -316,6 +342,19 @@ public class FlywayAutoConfigurationTests { ...@@ -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 @Configuration
protected static class FlywayDataSourceConfiguration { protected static class FlywayDataSourceConfiguration {
...@@ -395,13 +434,37 @@ public class FlywayAutoConfigurationTests { ...@@ -395,13 +434,37 @@ public class FlywayAutoConfigurationTests {
@Bean @Bean
@Order(1) @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); return mock(FlywayCallback.class);
} }
@Bean @Bean
@Order(0) @Order(0)
public FlywayCallback callbackTwo() { public FlywayCallback legacyCallbackTwo() {
return mock(FlywayCallback.class); return mock(FlywayCallback.class);
} }
......
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
<ehcache.version>2.10.5</ehcache.version> <ehcache.version>2.10.5</ehcache.version>
<ehcache3.version>3.5.2</ehcache3.version> <ehcache3.version>3.5.2</ehcache3.version>
<embedded-mongo.version>2.1.1</embedded-mongo.version> <embedded-mongo.version>2.1.1</embedded-mongo.version>
<flyway.version>5.0.7</flyway.version> <flyway.version>5.1.3</flyway.version>
<freemarker.version>2.3.28</freemarker.version> <freemarker.version>2.3.28</freemarker.version>
<elasticsearch.version>6.3.0</elasticsearch.version> <elasticsearch.version>6.3.0</elasticsearch.version>
<glassfish-el.version>3.0.0</glassfish-el.version> <glassfish-el.version>3.0.0</glassfish-el.version>
......
...@@ -2225,9 +2225,10 @@ more control, provide a `@Bean` that implements ...@@ -2225,9 +2225,10 @@ more control, provide a `@Bean` that implements
Flyway supports SQL and Java https://flywaydb.org/documentation/callbacks.html[callbacks]. 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` 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 folder. To use Java-based callbacks, create one or more beans that implement
`FlywayCallback` or, preferably, extend `BaseFlywayCallback`. Any such beans are `Callback`. Any such beans are automatically registered with `Flyway`. They can be
automatically registered with `Flyway`. They can be ordered by using `@Order` or by ordered by using `@Order` or by implementing `Ordered`. Beans that implement the
implementing `Ordered`. 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 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 uses that for migrations. If you like to use a different `DataSource`, you can create
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment