I would like to upgrade for my new projects to Spring Boot version 2.1.0, but I am limited with Oracle 11 database, which is supported by the Flyway 4.2.0 library. Everything runs normally on Spring Boot version 2.0.5 Release, but when moving to 2.1.0 release I get this error:
java.lang.NoClassDefFoundError:
org/flywaydb/core/api/configuration/FluentConfiguration
The POM configuration is as follows:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<ojdbc6.version>11.2.0.1</ojdbc6.version>
</properties>
<dependencies>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc6</artifactId>
<version>${ojdbc6.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
UPDATE
I am able to solve the problem via @Configuration (or of course add to the main class), but the thing is its a bug or feature? Prior to version 2.1.0 everything was done via autoconfiguration and it works out-of-box.
@Bean(initMethod = "migrate")
Flyway flyway() {
Flyway flyway = new Flyway();
flyway.setBaselineOnMigrate(true);
flyway.setDataSource("jdbc:oracle:thin:@localhost:1521:xe", "USER", "PASSWORD1");
return flyway;
}
I had the same problem with PostgreSQL 9.2, and used the following class to solve the problem.
Be aware though that all the custom properties you might set in the Spring Boot properties will be ignored, since that replaces the whole Flyway autoconfiguration by your own. So you might have to add some additional code to fit your needs.
@Configuration
class FlywayConfig {
@Bean
fun flyway(dataSource: DataSource): Flyway {
val flyway = Flyway()
flyway.dataSource = dataSource
return flyway
}
@Bean
fun flywayInitializer(flyway: Flyway): FlywayMigrationInitializer {
return FlywayMigrationInitializer(flyway, null)
}
/**
* Additional configuration to ensure that [EntityManagerFactory] beans depend on the
* `flywayInitializer` bean.
*/
@Configuration
class FlywayInitializerJpaDependencyConfiguration : EntityManagerFactoryDependsOnPostProcessor("flywayInitializer")
}
PS: this is Kotlin code, but you should be able to translate it to Java fairly easily.
Use the following dependency, it will be resolved by this.
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>5.2.3</version>
</dependency>
I made the configuration for Spring Boot 2.1.1 and had to redefine the bean FlywayDefaultDdlModeProvider as well.
@Configuration
@ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
public class LegacyFlywayAutoConfiguration {
@Bean
@Primary
public SchemaManagementProvider flywayDefaultDdlModeProvider(ObjectProvider<Flyway> flyways) {
return new SchemaManagementProvider() {
@Override
public SchemaManagement getSchemaManagement(DataSource dataSource) {
return SchemaManagement.MANAGED;
}
};
}
@Bean(initMethod = "migrate")
public Flyway flyway(DataSource dataSource) {
Flyway flyway = new Flyway();
flyway.setBaselineOnMigrate(true);
flyway.setDataSource(dataSource);
return flyway;
}
@Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
return new FlywayMigrationInitializer(flyway, null);
}
/**
* 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");
}
}
}
Using Javassist library you can instrument the FlywayDB library to log that the Oracle version is no longer supported instead of throwing a fatal exception (by wrapping the ensureDatabaseIsCompatibleWithFlywayEdition method call in a try-catch clause). In my case FlywayDB community edition (5.2.4) seems to work fine with Oracle 11.2 once I did it. This solution has its drawbacks but it was the best option in my case (database should be upgraded soon) so maybe someone can find it useful too. The below code should be run in your application before anything else ideally. Please use at your own risk.
public static void suppressIncompatibleDatabaseVersionCheck() {
try {
CtClass ctClass = ClassPool.getDefault().get("org.flywaydb.core.internal.database.base.Database");
ctClass.defrost();
CtMethod method = ctClass.getDeclaredMethod("ensureDatabaseIsCompatibleWithFlywayEdition");
CtClass etype = ClassPool.getDefault().get("java.lang.Exception");
method.addCatch("{ LOG.warn(\"Exception suppressed: \" + $e); return ;}", etype);
ctClass.toClass();
} catch (NotFoundException | CannotCompileException e) {
log.error("Could not instrument FlywayDB code.", e);
}
}