【问题标题】:Using JdbcTemplate in Flyway's Java migration files causes dependency cycle在 Flyway 的 Java 迁移文件中使用 JdbcTemplate 会导致依赖循环
【发布时间】:2020-02-18 05:59:19
【问题描述】:

从版本 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迁移文件中使用JdbcTemplateFlyway's document表明我可以使用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

  • Spring Boot:2.2.0

【问题讨论】:

  • 您遇到过这个问题吗?还是您找到任何解决方法,我遇到了完全相同的问题。
  • @FolgerFonseca 我通过找到修改SomeDAO 的方法解决了这个问题,这打破了问题设置的限制......

标签: java spring-boot flyway


【解决方案1】:

没有往下看堆栈,但我猜 Flyway 作为迁移工具不希望您在更改数据时结构化数据...

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我也对这个特性感到兴奋,并且非常失望地发现无法自动装配在某种程度上依赖于持久层的类。 但是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);
          }
      

      其次,创建这个 Class 从应用上下文中获取一个 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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-03
        • 2021-02-28
        • 1970-01-01
        • 2017-01-07
        • 1970-01-01
        • 1970-01-01
        • 2016-10-09
        相关资源
        最近更新 更多