Spring 使用 Flyway 4.2.0 启动 2.1.0

Spring Boot 2.1.0 with Flyway 4.2.0

我想将我的新项目升级到 Spring 引导版本 2.1.0,但我受限于 Oracle 11 数据库,Flyway 4.2.0[ 支持该数据库=24=]图书馆。一切在 Spring 启动版本 2.0.5 版本上正常运行,但是当移动到 2.1.0 版本时,我收到此错误:

java.lang.NoClassDefFoundError: 
org/flywaydb/core/api/configuration/FluentConfiguration

POM配置如下:

<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>

更新

我可以通过@Configuration 解决问题(当然也可以添加到主class),但问题是它是错误还是功能?在版本 2.1.0 之前,一切都是通过自动配置完成的,并且开箱即用。

@Bean(initMethod = "migrate")
Flyway flyway() {
    Flyway flyway = new Flyway();
    flyway.setBaselineOnMigrate(true);
    flyway.setDataSource("jdbc:oracle:thin:@localhost:1521:xe", "USER", "PASSWORD1");
    return flyway;
}

我用PostgreSQL 9.2遇到了同样的问题,用下面的class解决了这个问题。

请注意,您可能在 Spring 引导属性中设置的所有自定义属性都将被忽略,因为这会替换您自己的整个 Flyway 自动配置。因此,您可能需要添加一些额外的代码来满足您的需要。

@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:这是 Kotlin 代码,但您应该能够很容易地将它翻译成 Java。

我为 Spring Boot 2.1.1 进行了配置,并且还必须重新定义 bean FlywayDefaultDdlModeProvider。

@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");
        }

    }
}

使用 Javassist 库,您可以检测 FlywayDB 库以记录不再支持 Oracle 版本,而不是抛出致命异常(通过将 ensureDatabaseIsCompatibleWithFlywayEdition 方法调用包装在 try-catch 子句中)。在我的例子中,FlywayDB 社区版 (5.2.4) 在我完成后似乎可以很好地与 Oracle 11.2 一起使用。这个解决方案有它的缺点,但它是我的最佳选择(数据库应该很快升级)所以也许有人会发现它也很有用。理想情况下,下面的代码应该先 运行 在您的应用程序中。 请自行承担使用风险。

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);
    }
}

使用下面的依赖,就这样解决了

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
    <version>5.2.3</version>
</dependency>