Spring Boot 在Flyway的Java迁移文件中使用JdbcTemplate会导致依赖循环

gr8qqesn  于 2023-04-20  发布在  Spring
关注(0)|答案(4)|浏览(156)

从版本6.* 开始,Flyway支持Spring bean注入到java迁移文件中,并实现了JavaMigration接口。下面是我的示例:

@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迁移文件中使用JdbcTemplate,Flyway的文档显示我可以使用Context构建自己的JdbcTemplate,如下所示:

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

但不幸的是,我无法控制SomeDAO,它来自另一个模块,我无法触摸。
相关版本:

  • 飞行路线:6.0.6
  • Sping Boot :2.2.0
zte4gxcn

zte4gxcn1#

这就是Sping 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准备就绪后才启动。

zqry0prt

zqry0prt2#

我对这个特性也很兴奋,但很失望地发现,在某种程度上依赖于持久层的类是不可能自动连接的。但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。

fzwojiic

fzwojiic3#

没有向下看堆栈,但我猜Flyway作为迁移工具不希望您在更改数据时将其结构化。
其他(非数据访问)服务被注入并且可以被使用。
你可以从上下文中获取数据库连接,并使用jdbc模板进行更改。

xeufq47z

xeufq47z4#

我的情况是
1.我使用@Lazy注解来使相关的bean延迟初始化,
1.添加Spring配置:allow-circular-references: true到applition yml然后flyway可以启动并成功处理。
这就是我发现的为什么以及如何flyway导致循环引用:
就像上面的答案说的,Flyway migrate支持改变表结构的DDL,所以不希望你在迁移时注入jpa bean。
Spring(我的版本2.7.9)有FlywayAutoConfiguration注解@Import(DatabaseInitializationDependencyConfigurer.class),生成的数据源配置将依赖于flyway和flywayInitlizer,并且必须在flyway完成后进行数据源初始化。
但是有时候我们需要它来做一些事情,比如在Java代码中插入或更新数据(我的例子)。在Flyway的帮助下,我们可以同时受益于自动SQL迁移和定制的Java代码。

相关问题