Spring Boot: Hibernate and Flyway boot order
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();
Related videos on Youtube
user3636486
Updated on August 03, 2021Comments
-
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 about 8 yearsUnrelated 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 about 8 yearsDo you have any sample code of your second suggestion?
-
sofend over 7 yearsWe wish that were true. Actually Hibernate currently runs first and this is an open issue.
-
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 over 7 yearsHey, 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 over 6 yearsThis 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 over 6 yearsNice but i tried it and it cant locate my entitymanagerfactory by its name.
-
user3707816 over 6 yearsHave 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 over 5 yearsthis seems deprecated on the latest version of flyway,flyway.baselineOnMigrate = true
-
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 over 4 yearsthis 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 over 4 yearsYep, it does not work anymore. What is the alternative?
-
mota over 4 years@Kikin-Sama I had to turn off autoconfiguration
spring.flyway.enabled=false
and create new instance manuallyFlyway.configure().dataSource(dataSource).load();
-
icyerasor about 4 yearsThis 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 afterV3__update.sql
has already been applied to the database (if flyway detects that it has not been run yet). -
Baljinder Singh over 2 yearsDid this but after that getting, Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
-
Baljinder Singh over 2 yearstried 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 about 2 yearsWorks with Spring Boot 2.6.3 and 8.4.4
-
ancm about 2 yearsyou deserve a medal