Multiple Flyway-initialized data sources in a Spring Boot app – It’s (kinda) possible!

I love working with Spring Boot. I am working with it for about six months now, and yet it never ceases to amaze me with the amount of features bundled with it.

However, sometimes it tries to a good thing, but misses eventually. Let’s take the Flyway support for example. Flyway is used for easily migrating SQL queries on top of a provided schema on the application startup, so you can be sure that if the application started – the DB state will be as you expected it  to be. It also makes development environment setup (schema-wise) painless.  It’s a concept I was first exposed to while working on a RoR project, and for most of the SQL statements it is fitting. Spring supports easy integration with Flyway. Unfortunately it does not support more than one datasource initialized by Flyway. Also, if you try to define two Flyway beans you will get an exception.

In our production environment, we have a Spring Boot application with multiple data sources which we wanted to maintain with flyway, so we needed to find a way to to overcome that. The solution is actually simpler and maybe a bit disappointing: don’t use out-of-the-box support for Flyway and don’t define them as beans. Rather just programatically call the Flyway.migrate() method in an appropriate section of the startup.

public void migrateFlyway(FlywayProperties flywayProperties) {
    final Flyway flyway = new Flyway();
    flyway.setDataSource(
            flywayProperties.getUrl(),
            flywayProperties.getUser(),
            flywayProperties.getPassword());
    flyway.setSchemas(flywayProperties.getSchemas());
    flyway.setLocations(flywayProperties.getLocations().split(","));
    flyway.migrate();
}

In this code snippet, FlywayProperties is a class mapped by Spring Boot’s sweet configuration-to-bean mapping.

But where in my code should I trigger the Flyway migrations? A quite natural place would be just before the creation of a data source by Spring Boot. This way, if Flyway fails to migrate the data source will not be created on top of an old state of the schema, and moreover- application startup will fail (due to a FlywayException), which is in my opinion a desired result. So I ended up doing the following for every DataSource in the project:

    @Bean
    @ConfigurationProperties(prefix="datasources.master-db")
    public DataSource masterDataSource(MasterFlywayProperties masterFlywayProperties) {
        migrateFlyway(masterFlywayProperties);
        return DataSourceBuilder
                .create()
                .build();
    }

We are using Spring Boot’s smooth data source building and we initialized it beforehand using Flyway! Hooray!

2 thoughts on “Multiple Flyway-initialized data sources in a Spring Boot app – It’s (kinda) possible!

  1. Hi YWILKOF,
    Thanks for writing this article. I was trying to use this approach in my own project, and had a few questions around that.

    1. Do I correctly understand that the two methods you listed (“migrateFlyway” and “masterDataSource”) are both methods on the same class?

    2. If so, is that class called “MasterFlywayProperties” (which is the data type of the parameter to method masterDataSource()?

    3. Does that class inherit from org.springframework.boot.autoconfigure.flyway.FlywayProperties?

    4. Have you used this with two different sets of migrations? For example, the folder structure we have in mind is src/main/resources/db.migration/database_a/ and src/main/resources/db.migration/database_b/?

    5. In the @ConfigurationProperties annotation for method masterDataSource, the prefix = “datasources.master-db”. Does that mean you have somewhere else (perhaps in an applicationContext.xml) configured a data source like so: ? In other words, the prefix = “datasources.” + beanId?

    Thanks!,
    Adarsh

    1. Hi Adrash,

      1: They can be in the same class, in our case specifically we created and abstract AbstractDataSourceConfig class which has the migrateFlyway() method, so that every concrete data source config extends it calls the migrateFlyway method.

      2 + 3: We have our own abstract FlywayProperties class (not inheriting the spring FlywayProperties, though I guess it’s also possible) which is, like you say, extended for every data source.

      @Getter
      @Setter
      public abstract class FlywayProperties {
          private Boolean checkLocation;
          private String url;
          private String user;
          private String password;
          private String schemas;
          private String locations;
          private Boolean validateOnMigrate;
      }
      

      and a concrete class implementation:

      @Configuration
      @ConfigurationProperties(prefix = "flyways.master-db")
      public class MasterFlywayProperties extends FlywayProperties {
      }
      

      4. Yes. Since each datasource has its own FlywayProperties mapping, it is possible by using the the locations property to specify which datasource gets initialized with migrations from which folder(s).

      5. The properties for all datasources and flyway properties are defined in the application’s configuration file. Spring boot knows how to read different kinds of configuration file and map properties to objects. You can read about creating datasources directly from a properties file here

Leave a Reply

Your email address will not be published. Required fields are marked *