From 65c2d1f5e761a86da0a05ef191d203685dc9d473 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Mon, 22 Aug 2016 20:32:46 +0200 Subject: [PATCH 1/2] Add support for vendor specific Flyway migration locations This commit adds support for using `{vendor}` placeholder in `flyway.locations` configuration property value. See gh-6900 --- .../flyway/FlywayAutoConfiguration.java | 40 ++++++++++++++++++- .../flyway/FlywayAutoConfigurationTests.java | 12 ++++++ .../test/resources/db/vendors/h2/V1__init.sql | 0 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index 5974e6b9a3..0b5f64f9a6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -40,11 +40,14 @@ import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfigurat 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.jdbc.DatabaseDriver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.io.ResourceLoader; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.MetaDataAccessException; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.util.Assert; @@ -79,6 +82,8 @@ public class FlywayAutoConfiguration { @EnableConfigurationProperties(FlywayProperties.class) public static class FlywayConfiguration { + private static final String VENDOR_PLACEHOLDER = "{vendor}"; + private final FlywayProperties properties; private final ResourceLoader resourceLoader; @@ -126,7 +131,31 @@ public class FlywayAutoConfiguration { @Bean @ConfigurationProperties(prefix = "flyway") public Flyway flyway() { - Flyway flyway = new Flyway(); + Flyway flyway = new Flyway() { + + @Override + public void setLocations(String... locations) { + if (usesVendorLocation()) { + try { + String url = (String) JdbcUtils.extractDatabaseMetaData( + getDataSource(), "getURL"); + DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url); + if (vendor != DatabaseDriver.UNKNOWN) { + for (int i = 0; i < locations.length; i++) { + locations[i] = locations[i].replace( + VENDOR_PLACEHOLDER, + vendor.name().toLowerCase()); + } + } + } + catch (MetaDataAccessException e) { + throw new IllegalStateException(e); + } + } + super.setLocations(locations); + } + + }; if (this.properties.isCreateDataSource()) { flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(), this.properties.getPassword(), @@ -142,6 +171,15 @@ public class FlywayAutoConfiguration { return flyway; } + private boolean usesVendorLocation() { + for (String location : this.properties.getLocations()) { + if (location.contains(VENDOR_PLACEHOLDER)) { + return true; + } + } + return false; + } + @Bean @ConditionalOnMissingBean public FlywayMigrationInitializer flywayInitializer(Flyway flyway) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index c9426e6171..196ff1e286 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -234,6 +234,18 @@ public class FlywayAutoConfigurationTests { .isEqualTo(MigrationVersion.fromVersion("1")); } + @Test + public void useVendorDirectory() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "flyway.locations:classpath:db/vendors/{vendor}"); + registerAndRefresh(EmbeddedDataSourceConfiguration.class, + FlywayAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + Flyway flyway = this.context.getBean(Flyway.class); + assertThat(flyway.getLocations()).containsExactly( + "classpath:db/vendors/h2"); + } + private void registerAndRefresh(Class... annotatedClasses) { this.context.register(annotatedClasses); this.context.refresh(); diff --git a/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql b/spring-boot-autoconfigure/src/test/resources/db/vendors/h2/V1__init.sql new file mode 100644 index 0000000000..e69de29bb2 From 449043bf210d675d232baaed1d441ee1f4a81cba Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 22 Dec 2016 15:39:31 +0100 Subject: [PATCH 2/2] Polish contribution Closes gh-6900 --- .../flyway/FlywayAutoConfiguration.java | 76 ++++++++++--------- .../flyway/FlywayProperties.java | 6 +- .../flyway/FlywayAutoConfigurationTests.java | 7 +- spring-boot-docs/src/main/asciidoc/howto.adoc | 17 ++++- .../boot/jdbc/DatabaseDriver.java | 8 +- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java index 0b5f64f9a6..c49ea59ebb 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration.java @@ -82,8 +82,6 @@ public class FlywayAutoConfiguration { @EnableConfigurationProperties(FlywayProperties.class) public static class FlywayConfiguration { - private static final String VENDOR_PLACEHOLDER = "{vendor}"; - private final FlywayProperties properties; private final ResourceLoader resourceLoader; @@ -131,31 +129,7 @@ public class FlywayAutoConfiguration { @Bean @ConfigurationProperties(prefix = "flyway") public Flyway flyway() { - Flyway flyway = new Flyway() { - - @Override - public void setLocations(String... locations) { - if (usesVendorLocation()) { - try { - String url = (String) JdbcUtils.extractDatabaseMetaData( - getDataSource(), "getURL"); - DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url); - if (vendor != DatabaseDriver.UNKNOWN) { - for (int i = 0; i < locations.length; i++) { - locations[i] = locations[i].replace( - VENDOR_PLACEHOLDER, - vendor.name().toLowerCase()); - } - } - } - catch (MetaDataAccessException e) { - throw new IllegalStateException(e); - } - } - super.setLocations(locations); - } - - }; + Flyway flyway = new SpringBootFlyway(); if (this.properties.isCreateDataSource()) { flyway.setDataSource(this.properties.getUrl(), this.properties.getUser(), this.properties.getPassword(), @@ -171,15 +145,6 @@ public class FlywayAutoConfiguration { return flyway; } - private boolean usesVendorLocation() { - for (String location : this.properties.getLocations()) { - if (location.contains(VENDOR_PLACEHOLDER)) { - return true; - } - } - return false; - } - @Bean @ConditionalOnMissingBean public FlywayMigrationInitializer flywayInitializer(Flyway flyway) { @@ -220,6 +185,45 @@ public class FlywayAutoConfiguration { } + + private static class SpringBootFlyway extends Flyway { + + private static final String VENDOR_PLACEHOLDER = "{vendor}"; + + @Override + public void setLocations(String... locations) { + if (usesVendorLocation(locations)) { + try { + String url = (String) JdbcUtils.extractDatabaseMetaData( + getDataSource(), "getURL"); + DatabaseDriver vendor = DatabaseDriver.fromJdbcUrl(url); + if (vendor != DatabaseDriver.UNKNOWN) { + for (int i = 0; i < locations.length; i++) { + locations[i] = locations[i].replace( + VENDOR_PLACEHOLDER, + vendor.getId()); + } + } + } + catch (MetaDataAccessException ex) { + throw new IllegalStateException(ex); + } + } + super.setLocations(locations); + } + + + private boolean usesVendorLocation(String... locations) { + for (String location : locations) { + if (location.contains(VENDOR_PLACEHOLDER)) { + return true; + } + } + return false; + } + + } + /** * Convert a String or Number to a {@link MigrationVersion}. */ diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java index f63a4f6b71..d503f79e05 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayProperties.java @@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.flyway; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -38,9 +37,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties; public class FlywayProperties { /** - * Locations of migrations scripts. + * Locations of migrations scripts. Can contain the special "{vendor}" placeholder + * to use vendor-specific locations. */ - private List locations = new ArrayList(Arrays.asList("db/migration")); + private List locations = new ArrayList(Collections.singletonList("db/migration")); /** * Check that migration scripts location exists. diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java index 196ff1e286..aeca7652c0 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/flyway/FlywayAutoConfigurationTests.java @@ -237,19 +237,18 @@ public class FlywayAutoConfigurationTests { @Test public void useVendorDirectory() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, - "flyway.locations:classpath:db/vendors/{vendor}"); + "flyway.locations=classpath:db/vendors/{vendor},classpath:db/changelog"); registerAndRefresh(EmbeddedDataSourceConfiguration.class, FlywayAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); Flyway flyway = this.context.getBean(Flyway.class); - assertThat(flyway.getLocations()).containsExactly( - "classpath:db/vendors/h2"); + assertThat(flyway.getLocations()).containsExactlyInAnyOrder( + "classpath:db/vendors/h2", "classpath:db/changelog"); } private void registerAndRefresh(Class... annotatedClasses) { this.context.register(annotatedClasses); this.context.refresh(); - } @Configuration diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index c29bae81fe..091ae66d80 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -2062,9 +2062,20 @@ To automatically run Flyway database migrations on startup, add the The migrations are scripts in the form `V__.sql` (with `` an underscore-separated version, e.g. '`1`' or '`2_1`'). By default they live in a folder -`classpath:db/migration` but you can modify that using `flyway.locations` (a list). See -the Flyway class from flyway-core for details of available settings like schemas etc. In -addition Spring Boot provides a small set of properties in +`classpath:db/migration` but you can modify that using `flyway.locations`. You can also +add a special `{vendor}` placeholder to use vendor-specific scripts. Assume the following: + +[source,properties,indent=0] +---- + flyway.locations=db/migration/{vendor} +---- + +Rather than using `db/migration`, this configuration will set the folder to use according +to the type of the database (i.e. `db/migration/mysql` for MySql). The list of supported +database are available in {sc-spring-boot}/jdbc/DatabaseDriver.{sc-ext}[`DatabaseDriver`]. + +See also the Flyway class from flyway-core for details of available settings like schemas +etc. 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 will call `Flyway.migrate()` to perform the database migration. If you would like diff --git a/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java b/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java index 27bd772fef..22522b794a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java +++ b/spring-boot/src/main/java/org/springframework/boot/jdbc/DatabaseDriver.java @@ -182,13 +182,13 @@ public enum DatabaseDriver { private final String validationQuery; - DatabaseDriver(String id, String name, String driverClassName) { - this(id, name, driverClassName, null); + DatabaseDriver(String id, String productName, String driverClassName) { + this(id, productName, driverClassName, null); } - DatabaseDriver(String id, String name, String driverClassName, + DatabaseDriver(String id, String productName, String driverClassName, String xaDataSourceClassName) { - this(id, name, driverClassName, xaDataSourceClassName, null); + this(id, productName, driverClassName, xaDataSourceClassName, null); } DatabaseDriver(String id, String productName, String driverClassName,