Skip to content

Commit

Permalink
Add autoconfig support for Flyway migrations
Browse files Browse the repository at this point in the history
Flyway starts up with its default settings if it is on the classpath.
You can also ask Boot to barf if the migration scripts are missing.

Fixes gh-730
  • Loading branch information
Dave Syer committed May 2, 2014
1 parent 68e33b2 commit 5548b24
Show file tree
Hide file tree
Showing 20 changed files with 498 additions and 3 deletions.
5 changes: 5 additions & 0 deletions spring-boot-autoconfigure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
<artifactId>jackson-datatype-joda</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright 2012-2014 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 javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;

import com.googlecode.flyway.core.Flyway;

/**
* {@link EnableAutoConfiguration Auto-configuration} for Flyway database migrations.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass(Flyway.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class FlywayAutoConfiguration {

@Configuration
@ConditionalOnMissingBean(Flyway.class)
@EnableConfigurationProperties(FlywayProperties.class)
public static class LiquibaseConfiguration {

This comment has been minimized.

Copy link
@berlin-ab

berlin-ab May 20, 2014

Liquibase configuration should probably be renamed here.

This comment has been minimized.

Copy link
@philwebb

philwebb May 20, 2014

Member

I think this has been fixed already in commit 41395d0


@Autowired
private FlywayProperties properties = new FlywayProperties();

@Autowired
private ResourceLoader resourceLoader = new DefaultResourceLoader();

@Autowired
private DataSource dataSource;

@PostConstruct
public void checkLocationExists() {
if (this.properties.isCheckLocation()) {

Assert.state(!this.properties.getLocations().isEmpty(),
"Migration script locations not configured");
boolean exists = false;
for (String location : this.properties.getLocations()) {
Resource resource = this.resourceLoader.getResource(location);
exists = !exists && resource.exists();
}
Assert.state(exists, "Cannot find migrations location in: "
+ this.properties.getLocations()
+ " (please add migrations or check your Flyway configuration)");
}
}

@Bean
public Flyway flyway(DataSource dataSource) {
Flyway flyway = new Flyway();
flyway.setLocations(this.properties.getLocations().toArray(new String[0]));
flyway.setSchemas(this.properties.getSchemas().toArray(new String[0]));
flyway.setInitVersion(this.properties.getInitVersion());
flyway.setSqlMigrationPrefix(this.properties.getPrefix());
flyway.setSqlMigrationSuffix(this.properties.getSuffix());
flyway.setDataSource(dataSource);
flyway.migrate();
return flyway;
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2012-2014 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.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Configuration properties to configure Flyway.
*
* @author Dave Syer
*/
@ConfigurationProperties(prefix = "spring.flyway", ignoreUnknownFields = false)
public class FlywayProperties {

private List<String> locations = Arrays.asList("db/migrations");

private List<String> schemas = new ArrayList<String>();

private String prefix = "V";

private String suffix = ".sql";

private String initVersion = "1";

private boolean checkLocation;

public String getPrefix() {
return this.prefix;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public String getSuffix() {
return this.suffix;
}

public void setSuffix(String suffix) {
this.suffix = suffix;
}

public String getInitVersion() {
return this.initVersion;
}

public void setInitVersion(String initVersion) {
this.initVersion = initVersion;
}

public void setLocations(List<String> locations) {
this.locations = locations;
}

public List<String> getLocations() {
return this.locations;
}

public List<String> getSchemas() {
return this.schemas;
}

public void setSchemas(List<String> schemas) {
this.schemas = schemas;
}

public void setCheckLocation(boolean checkLocation) {
this.checkLocation = checkLocation;
}

public boolean isCheckLocation() {
return this.checkLocation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2012-2014 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.util.Arrays;

import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import com.googlecode.flyway.core.Flyway;

import static org.junit.Assert.assertEquals;

/**
* Tests for {@link LiquibaseAutoConfiguration}.
*
* @author Dave Syer
*/
public class FlywayAutoConfigurationTests {

@Rule
public ExpectedException expected = ExpectedException.none();

private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();;

@After
public void close() {
if (this.context != null) {
this.context.close();
}
}

@Test
public void testNoDataSource() throws Exception {
this.context.register(FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.expected.expect(BeanCreationException.class);
this.expected.expectMessage("No qualifying bean");
this.expected.expectMessage("DataSource");
this.context.refresh();
}

@Test
public void testDefaultFlyway() throws Exception {
this.context
.register(EmbeddedDataSourceConfiguration.class,
FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Flyway flyway = this.context.getBean(Flyway.class);
assertEquals("[classpath:db/migrations]", Arrays.asList(flyway.getLocations())
.toString());
}

@Test
public void testOverrideLocations() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.flyway.locations:classpath:db/changelog");
this.context
.register(EmbeddedDataSourceConfiguration.class,
FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Flyway flyway = this.context.getBean(Flyway.class);
assertEquals("[classpath:db/changelog]", Arrays.asList(flyway.getLocations())
.toString());
}

@Test
public void testOverrideSchemas() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context, "spring.flyway.schemas:public");
this.context
.register(EmbeddedDataSourceConfiguration.class,
FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
Flyway flyway = this.context.getBean(Flyway.class);
assertEquals("[public]", Arrays.asList(flyway.getSchemas()).toString());
}

@Test(expected = BeanCreationException.class)
public void testChangeLogDoesNotExist() throws Exception {
EnvironmentTestUtils.addEnvironment(this.context,
"spring.flyway.locations:no-such-dir");
this.context
.register(EmbeddedDataSourceConfiguration.class,
FlywayAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
}
}
Empty file.
8 changes: 7 additions & 1 deletion spring-boot-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<commons-dbcp.version>1.4</commons-dbcp.version>
<commons-pool.version>1.6</commons-pool.version>
<crashub.version>1.3.0-beta14</crashub.version>
<flyway.version>2.2.1</flyway.version>
<freemarker.version>2.3.20</freemarker.version>
<gemfire.version>7.0.1</gemfire.version>
<gradle.version>1.6</gradle.version>
Expand Down Expand Up @@ -80,7 +81,7 @@
<mongodb.version>2.11.4</mongodb.version>
<mysql.version>5.1.30</mysql.version>
<neo4j.version>2.0.2</neo4j.version>
<reactor.version>1.1.0.RELEASE</reactor.version>
<reactor.version>1.1.1.BUILD-SNAPSHOT</reactor.version>
<servlet-api.version>3.0.1</servlet-api.version>
<slf4j.version>1.7.7</slf4j.version>
<snakeyaml.version>1.13</snakeyaml.version>
Expand Down Expand Up @@ -146,6 +147,11 @@
<artifactId>jackson-datatype-joda</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.flyway</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
Expand Down
14 changes: 14 additions & 0 deletions spring-boot-docs/src/main/asciidoc/howto.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,20 @@ Spring Boot works fine with higher level migration tools http://flywaydb.org/[Fl
Flyway because it is easier on the eyes, and it isn't very common to need platform
independence: usually only one or at most couple of platforms is needed.

[[howto-execute-flyway-database-migrations-on-startup]]
==== Execute Flyway database migrations on startup
To automatically run Flyway database migrations on startup, add the
`spring-boot-starter-flyway` to your classpath.

The migrations are scripts in the form `V<VERSION>__<NAME>.sql` (with
`<VERSION>` an underscore-separated version, e.g. "1" or "2_1"). By
default they live in a folder `classpath:db/migrations` but you can
modify that using `flyway.locations` (a list). See
{sc-spring-boot-autoconfigure}/flyway/FlywayProperties.{sc-ext}[`FlywayProperties`]
for details of available settings like schemas etc.

There is a {github-code}/spring-boot-samples/spring-boot-sample-flyway[Flyway sample] so
you can see how to set things up.


[[howto-execute-liquibase-database-migrations-on-startup]]
Expand Down
3 changes: 3 additions & 0 deletions spring-boot-docs/src/main/asciidoc/using-spring-boot.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ and Hibernate.
|`spring-boot-starter-jdbc`
|JDBC Database support.

|`spring-boot-starter-flyway`
|Support for Flyway database migrations.

|`spring-boot-starter-liquibase`
|Support for Liquibase database migrations.

Expand Down
1 change: 1 addition & 0 deletions spring-boot-samples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<module>spring-boot-sample-data-mongodb</module>
<module>spring-boot-sample-data-redis</module>
<module>spring-boot-sample-data-rest</module>
<module>spring-boot-sample-flyway</module>
<module>spring-boot-sample-integration</module>
<module>spring-boot-sample-jetty</module>
<module>spring-boot-sample-liquibase</module>
Expand Down
Loading

0 comments on commit 5548b24

Please sign in to comment.