Reputation: 1573
I have multiple Flyway
datasources and need to implement FlywayMigrationStrategy
to them. Each datasource has their own flyway_migration
table etc.
But when I create FlywayMigrationStrategy
it will not be called.
This doesn't work:
@Bean
public FlywayMigrationStrategy cleanMigrateStrategy() { ...
This works:
@PostConstruct
public void cleanBeforeMigrate(
@Qualifier("dpaFlyway") Flyway dpaflyway,
@Qualifier("flyway") Flyway flyway) {
dpaflyway.clean();
dpaflyway.migrate();
flyway.clean();
flyway.migrate();
}
Are there better options?
Upvotes: 2
Views: 4491
Reputation: 1227
Why do you implement @PostContruct
method? As I see, FlywayMigrationStrategy
is a functional interface and I guess that your code should look like this:
@Bean
public FlywayMigrationStrategy cleanMigrateStrategy() {
FlywayMigrationStrategy strategy = new FlywayMigrationStrategy() {
@Override
public void migrate(Flyway flyway) {
flyway.clean();
flyway.migrate();
}
};
return strategy;
}
Ok, I have some research and now can explain the answer to your question.
First of all, let's check the FlywayAutoConfiguration
When you use configuring flyway migration via a property file, the configuration works, creates and configures Flyway instances and migrates base. All of these are configured in FlywayConfiguration
. Let's look on the condition annotation on the configuration
@ConditionalOnMissingBean(Flyway.class)
This means that configuration isn't created if bean for class Flyway.class
already exists in the spring context. Ok, next, the configuration creates only to beans
@Bean
public Flyway flyway(FlywayProperties properties, DataSourceProperties dataSourceProperties,
ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
@FlywayDataSource ObjectProvider<DataSource> flywayDataSource,
ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
ObjectProvider<JavaMigration> javaMigrations, ObjectProvider<Callback> callbacks)
.....
@Bean
@ConditionalOnMissingBean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
It's not so important how the config configures the flyway
bean, but it important that it creates FlywayMigrationInitializer
, which initiate migration. As you can see, FlywayMigrationStrategy
is set to FlywayMigrationInitializer
as constructor argument. And it is he who uses the MigrationStrategy if it was set, otherwise just execute flyway.migrate()
. (see source code)
Ok, now we know how it works in general, let's see in your code.
You create to flyway instances in your main configuration:
@Bean(initMethod = "migrate")
@FlywayDataSource
public Flyway firstFlyway(DataSource dataSource) {
return new Flyway(
new FluentConfiguration()
.locations("db/first-migration")
.schemas("first")
.outOfOrder(true)
.dataSource(dataSource)
);
}
@Bean(initMethod = "migrate")
@FlywayDataSource
public Flyway secondFlyway(@Qualifier("secondDataSource") DataSource dataSource) {
return new Flyway(
new FluentConfiguration()
.dataSource(dataSource)
.schemas("second")
.outOfOrder(true)
.locations("db/second-migration")
);
}
Since you already create Flyway instances, FlywayConfiguration
isn't created( due to the condition) and Initializer beans for your Flyway
beans also aren't created. As a result migrations aren't executed and you need to add (initMethod = "migrate")
to bean declaration for starting the migration.
Also, I think the @FlywayDataSource
is not required and do nothing.
Let's now go to your test configuration.
When you creating FlywayMigrationStrategy
, it works correctly, but no one use the strategy (in your main config file you call Flyway.migrate
just as initMethod and MigrationInitializer isn't created). As a result, the strategy isn't executed.
In your working example, you added calling clean and migrate in TestConfiguration
postconstruct method. And it works because postconstruct method is executed after configuration was created. BUT if you debug your code, you will see that migrate
method is executed twice for each Flyway instances: as a bean initMethod
and from Testconfiguration postcontruct method. I'm not sure that it is what you want.
Ok, for fixing it, I suggest remove (initMethod = "migrate")
and @FlywayDataSource
, create FlywayMigrationInitializer
bean for each Flyway
bean in your main configuration and implement FlywayMigrationStrategy
in your test config.
@Bean
public FlywayMigrationInitializer flywayInitializer(@Qualifier(...) Flyway flyway,
ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
return new FlywayMigrationInitializer(flyway, migrationStrategy.getIfAvailable());
}
Upvotes: 6