FlywayConfiguration.java
package com.stocks.stockease.config;
import javax.sql.DataSource;
import org.flywaydb.core.Flyway;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.jspecify.annotations.NonNull;
/**
* Runs Flyway migrations before JPA initializes, so that Hibernate's schema validation
* ({@code ddl-auto=validate}) never runs against an unmigrated database.
*
* <p>Introduced for an initialization-order conflict in Spring Boot 3.5 and retained after
* the Spring Boot 4 upgrade. Migration scripts live in {@code classpath:db/migration/} and
* follow the {@code V{VERSION}__{DESCRIPTION}.sql} naming convention
* (e.g. {@code V1__init_schema.sql}).
*/
@Configuration
@ConditionalOnClass(Flyway.class)
public class FlywayConfiguration {
// spring.flyway.enabled=false disables migrations (used in test profiles)
@Value("${spring.flyway.enabled:true}")
private boolean flywayEnabled;
/**
* Creates a {@link Flyway} instance and executes pending migrations immediately.
*
* <p>{@code initMethod="migrate"} forces Spring to run migrations <em>before</em>
* {@code EntityManagerFactory} is created, guaranteeing the schema exists when validation runs.
* {@code @Primary} prevents ambiguity if another {@link Flyway} bean exists on the classpath.
*
* @param dataSource the application {@link DataSource} injected by Spring
* @return the configured {@link Flyway} instance after all pending migrations have run
* @throws Exception if the {@link DataSource} connection fails after all retries
*/
@Primary
@Bean(initMethod = "migrate")
public Flyway flyway(@NonNull DataSource dataSource) throws Exception {
if (!flywayEnabled) {
return Flyway.configure().dataSource(dataSource).load();
}
Flyway flyway = Flyway.configure()
.dataSource(dataSource)
.locations("classpath:db/migration")
.baselineOnMigrate(true)
.cleanDisabled(true) // prevents accidental database wipe in production
.outOfOrder(true)
.connectRetries(20)
.connectRetriesInterval(2)
.load();
flyway.migrate();
return flyway;
}
}