在Flyway的Java迁移文件中使用JdbcTemplate会导致依赖周期

时间:2019-10-22 06:30:23

标签: java spring-boot flyway

从6. *版开始,Flyway支持通过实现JavaMigration接口将Spring bean注入到Java迁移文件中。这是我的示例:

@Component
public class V1_201809261821__some_migration extends BaseJavaMigration {

    @Autowired
    private SomeDAO someDAO;

    @Override
    public void migrate(Context context) throws Exception {
        someDAO.doSomething();
    }
}

启动时,它抱怨:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   v1_201809261821__some_migration (field private SomeDAO V1_201809261821__some_migration.someDAO)
┌─────┐
|  someDAO (field private org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate someDAO.namedParameterJdbcTemplate)
↑     ↓
|  flywayInitializer defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]
↑     ↓
|  flyway defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]
↑     ↓
|  v1_201809261821__some_migration (field private SomeDAO V1_201809261821__some_migration.someDAO)
└─────┘

似乎我不能在Java迁移文件中使用JdbcTemplateFlyway's document表明我可以使用JdbcTemplate来构造自己的Context,例如:

public void migrate(Context context) {
        new JdbcTemplate(new SingleConnectionDataSource(context.getConnection(), true))
                .execute("INSERT INTO test_user (name) VALUES ('Obelix')");
}

但是不幸的是,我无法控制SomeDAO,它来自我无法接触的另一个模块。

相关版本:

  • 空中通道:6.0.6

  • Spring Boot:2.2.0

3 个答案:

答案 0 :(得分:0)

没有低头看栈,但我想Flyway作为迁移工具不希望您在更改数据时对数据进行结构化...

其他(非数据访问)服务被注入并可以使用。

您从上下文中获取数据库连接以使用jdbc模板对其进行更改。

答案 1 :(得分:0)

这就是Spring Boot决定集成Flyway的方式。 在默认的自动配置中,在完成Flyway迁移之前,您不能对数据库执行任何操作。这是一个明智但明智的选择。

看看FlywayAutoConfiguration的源代码。有一个相当讨厌的技巧来确保在Flyway准备就绪之前没有人可以使用JdbcTemplate:

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

        }

要实现您的目标,您将必须禁用此自动配置(spring.autoconfigure.exclude)并自己编写Flyway配置。您可以从FlywayAutoConfiguration的源代码开始,但要删除棘手的方法。

但是,您将必须添加类似的依赖项技巧,以确保只有在自定义Flyway准备就绪后才能启动服务/工作。

答案 2 :(得分:0)

我也对这个功能感到兴奋,并且很失望地发现无法自动连接以某种方式依赖于持久层的类。 但是Flyway Spring Boot Autowired Beans with JPA Dependency中描述的解决方案仍然有效:

首先扩展FlywayConfiguration

@Configuration
@ComponentScan
@ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
class DatabaseMigrationConfiguration extends FlywayConfiguration {

    @Override
    public Flyway flyway(FlywayProperties properties, DataSourceProperties dataSourceProperties,
        ResourceLoader resourceLoader, ObjectProvider<DataSource> dataSource,
        ObjectProvider<DataSource> flywayDataSource,
        ObjectProvider<FlywayConfigurationCustomizer> fluentConfigurationCustomizers,
        ObjectProvider<JavaMigration> javaMigrations,
        ObjectProvider<Callback> callbacks) {
        return super.flyway(properties, dataSourceProperties, resourceLoader, dataSource, flywayDataSource, fluentConfigurationCustomizers,
            javaMigrations, callbacks);
    }

    @Primary
    @Bean(name = "flywayInitializer")
    @DependsOn({ "springUtility" })
    @ConditionalOnProperty(prefix = "spring.flyway", name = "enabled", matchIfMissing = true)
    public FlywayMigrationInitializer flywayInitializer(Flyway flyway,
        ObjectProvider<FlywayMigrationStrategy> migrationStrategy) {
        return super.flywayInitializer(flyway, migrationStrategy);
    }

第二,创建该类以从应用程序上下文中获取Bean:

@Component
public class SpringUtility implements ApplicationContextAware {

    @Autowired
    private static ApplicationContext applicationContext;

    public void setApplicationContext(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    /*
        Get a class bean from the application context
     */
    static <T> T getBean(final Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

现在,您可以在Java迁移类(extends BaseJavaMigration)中使用该类来获取所需的任何Bean。