Using JdbcTemplate in Flyway's Java migration files causes dependency cycle

flyway placeholders
flyway checksum
flyway naming convention
flyway java migration
flyway spring boot
flyway example
flyway versioning best practices
flyway repeatable migration order

Since version 6.*, Flyway supports Spring bean injection into java migration files with JavaMigration interface implemented. Here is my example:

@Component
public class V1_201809261821__some_migration extends BaseJavaMigration {

    @Autowired
    private SomeDAO someDAO;

    @Override
    public void migrate(Context context) throws Exception {
        someDAO.doSomething();
    }
}

When startup, it complains that:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   v1_201809261821__some_migration (field private SomeDAO V1_201809261821__some_migration.someDAO)
┌─────┐
|  someDAO (field private org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate someDAO.namedParameterJdbcTemplate)
↑     ↓
|  flywayInitializer defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]
↑     ↓
|  flyway defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]
↑     ↓
|  v1_201809261821__some_migration (field private SomeDAO V1_201809261821__some_migration.someDAO)
└─────┘

It seems that I can't use JdbcTemplate in Java migration files, Flyway's document shows that I can construct my own JdbcTemplate using Context like:

public void migrate(Context context) {
        new JdbcTemplate(new SingleConnectionDataSource(context.getConnection(), true))
                .execute("INSERT INTO test_user (name) VALUES ('Obelix')");
}

But unfortunately I have no control of SomeDAO, it's from another module I can't touch.

Related versions:

  • Flyway: 6.0.6

  • Spring Boot: 2.2.0

Did not look down the stack, but I guess Flyway as a migration tool does not want you to have the data structured while changing it...

Other (non data access) services are injected and can be used.

You get the database connection from the context to change it using jdbc template.

Migrations - Migrations, Example 1 with JdbcTemplate. use of org.flywaydb.core.internal.dbsupport. JdbcTemplate in project flyway by flyway. the� Add a dependency of your JDBC driver to the flyway-commandline .pom if you wish the driver to be automatically shipped with the Flyway product. In flyway.conf, document the format of the JDBC connection url for your database. This is not necessary to make Flyway work but it will help adoption of your database!

This is how Spring Boot decided to integrate Flyway. In the default autoconfiguration, you cannot do anything with the database before the Flyway migration is done. This is a sensible, but opinionated choice.

Take a look at the source code of FlywayAutoConfiguration. There's a rather nasty trick to ensure that noone can use JdbcTemplate before Flyway is ready:

        /**
         * Additional configuration to ensure that {@link JdbcOperations} beans depend on
         * the {@code flywayInitializer} bean.
         */
        @Configuration
        @ConditionalOnClass(JdbcOperations.class)
        @ConditionalOnBean(JdbcOperations.class)
        protected static class FlywayInitializerJdbcOperationsDependencyConfiguration
                extends JdbcOperationsDependsOnPostProcessor {

            public FlywayInitializerJdbcOperationsDependencyConfiguration() {
                super("flywayInitializer");
            }

        }

To achieve your goals, you will have to disable this autoconfiguration (spring.autoconfigure.exclude) and write the Flyway configuration yourself. You can start with the source code of FlywayAutoConfiguration, but with the tricky methods removed.

However, you will have to add a similar dependency trick, to ensure that your services/jobs are started only after your custom Flyway is ready.

Examples with JdbcTemplate, Prior to using Flyway, all of the schema updates that had to be done were delivered to the client SpringJdbcMigration makes a JdbcTemplate object available for database interaction. flyway.url, flyway.user, flyway.password are optional. If we have a separate user for database migration then we can specify using those properties. flyway automatically find the default data source when we will not specify flyway.url, flyway.user, flyway.password.

I was excited about this feature as well, and was very disappointed to find out that it is not possible to autowire classes which in some way are dependent on persistence layer. But the solution described in Flyway Spring Boot Autowired Beans with JPA Dependency still works:

First extend FlywayConfiguration:

@Configuration
@ComponentScan
@ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
class DatabaseMigrationConfiguration extends FlywayConfiguration {

    @Override
    public Flyway flyway(FlywayProperties properties, DataSourceProperties dataSourceProperties,
        ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
        ObjectProvider<DataSource> flywayDataSource,
        ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
        ObjectProvider<JavaMigration> javaMigrations,
        ObjectProvider<Callback> callbacks) {
        return super.flyway(properties, dataSourceProperties, resourceLoader, dataSource, flywayDataSource, fluentConfigurationCustomizers,
            javaMigrations, callbacks);
    }

    @Primary
    @Bean(name = "flywayInitializer")
    @DependsOn({ "springUtility" })
    @ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
    public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
        ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
        return super.flywayInitializer(flyway, migrationStrategy);
    }

Second, create this Class to get a Bean from application context:

@Component
public class SpringUtility implements ApplicationContextAware {

    @Autowired
    private static ApplicationContext applicationContext;

    public void setApplicationContext(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /*
        Get a class bean from the application context
     */
    static <T> T getBean(final Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

Now you can use this class in your java migration class (extends BaseJavaMigration) to get any Bean you want.

Integrating Flyway In A Spring Framework Application, Using Spring Boot 2.0.2.RELEASE I set up a simple project that uses Flyway for migrations and JDBC� Spring Boot simplifies database migrations by providing integration with Flyway, one of the most widely used database migration tools.This guide presents various options of using Flyway as part of a Spring Boot application, as well as running it within a CI build.

Database migrations may not have run before JdbcTemplate is , I find it weird that the FlyWay migration isn't executed before I am able to use the DataSource or the FlyWay migration doesn't run before the datasource or jdbctemplate is used. Write Flyway migrations with Java if needed. If you need custom logic for your next database migration which may result in a complex SQL script, you can also use Java and JDBC to manage migrations. You just have to follow the Flyway naming convention, place a class in the db.migration package and extend the BaseJavaMigration class:

FlyWay migration doesn't run before the datasource or , This guide presents various options of using Flyway as part of a Spring Boot JdbcTemplate; import� This can be configured using the placeholderPrefix and placeholderSuffix properties. See Placeholders for more details. Does Flyway depend on Spring? No. Flyway has zero required dependences. If you have Spring Jdbc on the classpath, Flyway will be able to load Java migrations making use of Spring's convenient JdbcTemplate class.

One-Stop Guide to Database Migration with Flyway and Spring , Example of Java Custom code to Migration Database using Flyway Migration with Spring MVC or Spring boot JdbcTemplate will be passed so we can execute the query using that. Flyway Configuration using Spring Boot. Spring Boot comes with out-of-the-box support for Flyway, all you need to do is to add a line into build.gradle: compile "org.flywaydb:flyway-core:4.0.3" Flyway will be automatically called when application starts. If you use H2 for unit tests, Flyway will be called for H2 initialization as well.