Spring Boot 2.1.0 和 Flyway 4.2.0

Posted

技术标签:

【中文标题】Spring Boot 2.1.0 和 Flyway 4.2.0【英文标题】:Spring Boot 2.1.0 with Flyway 4.2.0 【发布时间】:2019-04-19 23:24:48 【问题描述】:

我想将我的新项目升级到 Spring Boot 版本 2.1.0,但我受限于 Oracle 11 数据库,Flyway 4.2.0 库支持该数据库。一切都在 Spring Boot 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 解决问题(或者当然添加到主类),但问题是它的错误还是功能?在 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;

【问题讨论】:

这根本不是错误。 Spring Boot 依赖于 Flyway 5.2.1,它的 API 与您使用的旧版本不同。 是的,我注意到了,但不能以某种方式将它排除在 pom.xml 中以支持旧版本吗?我可以找到正确的依赖项,但它正在寻找这个 FluentConfiguration 类。 当然可以。但这不会改变配置 Flyway 的 Spring Boot 代码,它使用仅存在于最新版本 Flyway 中的新类。打个比方,如果你用 Java 8 编译使用 java.util.stream 的类,然后尝试在 Java 7 上运行该代码,那是行不通的,因为 Java 7 中不存在 java.util.stream . 您是如何测试您的应用的? 【参考方案1】:

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

请注意,您可能在 Spring Boot 属性中设置的所有自定义属性都将被忽略,因为这会用您自己的方式替换整个 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。

【讨论】:

嗨。是的,这正是我试图做的,请看我的评论;以编程方式进行配置。问题是,这是正确的方法吗?为什么以这种方式更改自动配置?如果我不能使用 application.properties,我如何为不同的环境配置不同的数据源?谢谢 你可以使用application.properties。只是不要期望 flyway 属性会产生任何影响,因为 Spring Boot 不会再读取和应用它们了。其他与 Flyway 无关的,仍然可以使用。 如何让它在 Spring Boot 测试中工作?我需要为 flyway 加载不同的属性以进行测试,但是 Flyway bean 不可用,或者当 Ier 手动创建 Flyway bean 时,其他 bean 会丢失。谢谢 @JBNizet 作为一个不了解 Kotlin 的人,除了最后一行,一切都可以理解。 @Younes_EO 最后一行:`@Configuration public static class FlywayInitializerJpaDependencyConfiguration extends EntityManagerFactoryDe​​pendsOnPostProcessor public FlywayInitializerJpaDependencyConfiguration() super("flywayInitializer"); `【参考方案2】:

我为 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");
        

    

【讨论】:

你可以在@SpringBootTest 中使用它吗? 是的,被覆盖的配置本身适用于@SpringBootTest。但通常你不知道t want to use Oracle database for integration test, so I dont 仅将其用于测试。我正在为@SpringBootTest 使用 H2 数据库,最新的 Flyway 版本没有任何问题。虽然,为了兼容性,我没有为集成测试创建自己的 Flyway 配置。 是的,这就是我想做的,但不知何故我想不出正确的方法。如果我在测试/资源中包含 application.yml,则 Flyway Bean 被实例化,并为版本 5+ 实例化。如果我将@Configuration 或@TestCONfiguration 放在某处,那么我可以通过编程方式创建它,但是其他Bean(Daos、Services)为空,因为我也需要手动创建它们。我只想手动创建 Flyway Bean,一切都留在 Spring 上。 必须将 spring.main.allow-bean-definition-overriding=true 添加到 application.properties 但它有效!【参考方案3】:

使用 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);
    

【讨论】:

【参考方案4】:

使用下面的依赖,会被this解析。

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

【讨论】:

正如我在问题中提到的,由于不支持 Oracle 11,我不能使用高于 4.2.0 的 flyway 版本 对我来说同样不能使用更高版本的flyway,除非你说flyway-core 5与ojdbc6 11和oracle 11兼容(不是)

以上是关于Spring Boot 2.1.0 和 Flyway 4.2.0的主要内容,如果未能解决你的问题,请参考以下文章

重磅Spring Boot 2.1.0 权威发布

Spring Boot Admin 2.1.0 学习笔记

Spring-Boot Banner

spring-boot 2.1.0 mongo - CodecConfigurationException:找不到类 java.time.Year 的编解码器

rocketmq-spring-boot-starter 2.1.0 事务消息 txProducerGroup 移除解读

Spring Boot 2.1.0 @Controller 不工作?