Spring Boot: Hibernate and Flyway boot order

24,160

Solution 1

I had the same issue.

I wanted my schema to be created by hibernate because of it's database independence. I already went through the trouble of figuring out a nice schema for my application in my jpa classes, I don't like repeating myself.

But I want some data initialization to be done in a versioned manner which flyway is good at.

Spring boot runs flyway migrations before hibernate. To change it I overrode the spring boot initializer to do nothing. Then I created a second initializer that runs after hibernate is done. All you need to do is add this configuration class:

import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
public class MigrationConfiguration {


    /**
     * Override default flyway initializer to do nothing
     */
    @Bean
    FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
        return new FlywayMigrationInitializer(flyway, (f) ->{} );
    }


    /**
     * Create a second flyway initializer to run after jpa has created the schema
     */
    @Bean
    @DependsOn("entityManagerFactory")
    FlywayMigrationInitializer delayedFlywayInitializer(Flyway flyway) {
        return new FlywayMigrationInitializer(flyway, null);
    }


}

That code needs java 8, If you have java 7 or earlier, replace (f)->{} with an inner class that implements FlywayMigrationStrategy

Of course you can do this in xml just as easily.

Make sure to add this to your application.properties:

flyway.baselineOnMigrate = true

Solution 2

All SQL migrations will start after Hibernate creates all the tables.

Spring Boot 2.2.2, Flyway 6.0.8

To disable boot for Flyway, insert into resources/application.properties:

spring.flyway.enabled=false

Create separate configuration for Flyway to make it load when Hibernate is ready:

@Configuration
public class FlywayConfiguration {

    @Autowired
    public FlywayConfiguration(DataSource dataSource) {
        Flyway.configure().baselineOnMigrate(true).dataSource(dataSource).load().migrate();
    }
}

Start your migration scripts from version 2:

resources/db.migration/V2__fill-tables.sql

V1 is used as a baseline, V1 file will be ignored.

Solution 3

Spring Boot auto-configuration of Flyway ensures that database migrations have run before Hibernate is initialised. In other words, you can't rely on Flyway auto-configuration and use Flyway to populate tables created by Hinernate.

One solution is to fully embrace Flyway and use it to both create the tables and populate them. You can then switch off Hibernate's table creation (spring.jpa.hibernate.ddl-auto=none). This approach is more robust as it will allow your database to evolve more easily. This is what I would recommend that you do.

Another solution is to disable auto-configuration of Flyway (flyway.enabled=false) and to configure it your self. You can then configure Flyway to depend on Hibernate so that Hibernate has created the tables before Flyway tries to populate them.

Solution 4

For more recent users that use Spring Boot +2.1 and as @mota commented into @user3707816's answer, you can use spring.flyway.enabled=false in application.properties and then create a new instance manually:

Flyway.configure().dataSource(dataSource)
                .baselineOnMigrate(true)
                .schemas(PG_DATABASE_SCHEMA)//optional, by default is public
                .load().migrate();
Share:
24,160

Related videos on Youtube

user3636486
Author by

user3636486

Updated on August 03, 2021

Comments

  • user3636486
    user3636486 almost 3 years

    I have created Spring application. Pom xml is attached.

    It has a config like this (below) and some db/migration/V1__init.sql for Flyway db migration tool.

    It has hsqldb in-memory database and it is created after application is started. It is clean after creation.

    I want Hibernate to create a schema based on entity classes and then Flyway fills the tables. Now Flyway starts V1__init.sql before tables is created and throw an exception. How can I change this order or what solution can I do?

    spring.datasource.testWhileIdle = true
    spring.datasource.validationQuery = SELECT 1
    spring.jpa.show-sql = true
    spring.jpa.hibernate.ddl-auto = create-drop
    spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.HSQLDialect
    

    pom.xml:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.3.RELEASE</version>
        <relativePath/>
    </parent>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.3.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.hsqldb</groupId>
            <artifactId>hsqldb</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.3.11.Final</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
    
        <!-- For using 'LEGACYHTML5' mode in Thymeleaf -->
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.21</version>
        </dependency>
        <dependency>
            <groupId>xml-apis</groupId>
            <artifactId>xml-apis</artifactId>
            <version>1.4.01</version>
        </dependency>
    
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>1.3.3.RELEASE</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    
    • Andy Wilkinson
      Andy Wilkinson about 8 years
      Unrelated to your question, but you should remove many of the versions from your pom and let Boot configure them for you. And you certainly shouldn't mix versions of Spring Boot itself (you have 1.3.2.RELEASE and 1.3.3.RELEASE).
  • Gabriel Oliveira
    Gabriel Oliveira about 8 years
    Do you have any sample code of your second suggestion?
  • sofend
    sofend over 7 years
    We wish that were true. Actually Hibernate currently runs first and this is an open issue.
  • Andy Wilkinson
    Andy Wilkinson over 7 years
    @sofend I believe you are mistaken and the question suggests so too. We also have tests in Boot that verify that Flyway migrations run before Hibernate. What makes you think that Hibernate runs first?
  • sofend
    sofend over 7 years
    Hey, if so, that's welcome news. I've got what I assume to be a pretty vanilla configuration running on the latest Spring Boot release (1.4.3.RELEASE). I'm using the latest test framework annotations. I'm happy to share some of my config. Or, if you've got it working, probably makes sense to see what you have and learn that way. (Are you using Spring Boot, or just straight Spring?) The issue I'm running into is not running Flyway to do data population (which is what the person who started this thread was doing). I'm using Flyway to migrate the schema and want Hibernate to validate it.
  • Paul Hilliar
    Paul Hilliar over 6 years
    This works great. Instead of @DependsOn I prefer to just declare a parameter (then it's strongly typed) but this overriding of the flyway initialisation is extremely useful. Thanks!
  • djangofan
    djangofan over 6 years
    Nice but i tried it and it cant locate my entitymanagerfactory by its name.
  • user3707816
    user3707816 over 6 years
    Have you added spring data jpa to your pom.xml? check what auto-configuration is doing. Or like Paul Hilliar said inject it by type
  • Amare
    Amare over 5 years
    this seems deprecated on the latest version of flyway,flyway.baselineOnMigrate = true
  • krzakov
    krzakov about 5 years
    @AndyWilkinson Flyway ensures that database migrations have run before Hibernate is initialised, have you managed to find particular piece of code validating that statement? This is not mentioned within their documentation
  • kmek
    kmek over 4 years
    this no longer works for Spring Boot 2.1.8. After upgrading from 2.1.7 to 2.1.8, the app cannot start with an error claiming: Circular depends-on relationship between 'delayedFlywayInitializer' and 'entityManagerFactory'
  • Kikin-Sama
    Kikin-Sama over 4 years
    Yep, it does not work anymore. What is the alternative?
  • mota
    mota over 4 years
    @Kikin-Sama I had to turn off autoconfiguration spring.flyway.enabled=false and create new instance manually Flyway.configure().dataSource(dataSource).load();
  • icyerasor
    icyerasor about 4 years
    This has nothing to do with execution order of Flyway/Hibernate.. so just for clarification: this setting will allow migrations to run out of order. I.e. V2__migration.sql might run after V3__update.sql has already been applied to the database (if flyway detects that it has not been run yet).
  • Baljinder Singh
    Baljinder Singh over 2 years
    Did this but after that getting, Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
  • Baljinder Singh
    Baljinder Singh over 2 years
    tried this but getting, Parameter 0 of method flywayInitializer in com.ninja.app.MigrationConfiguration required a bean of type 'org.flywaydb.core.Flyway' that could not be found
  • no id
    no id about 2 years
    Works with Spring Boot 2.6.3 and 8.4.4
  • ancm
    ancm about 2 years
    you deserve a medal