在 Flyway 的 Java 迁移文件中使用 JdbcTemplate 会导致依赖循环
Posted
技术标签:
【中文标题】在 Flyway 的 Java 迁移文件中使用 JdbcTemplate 会导致依赖循环【英文标题】:Using JdbcTemplate in Flyway's Java migration files causes dependency cycle 【发布时间】: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迁移文件中使用JdbcTemplate
,Flyway'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
的方法解决了这个问题,这打破了问题设置的限制......
【参考方案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。
【讨论】:
以上是关于在 Flyway 的 Java 迁移文件中使用 JdbcTemplate 会导致依赖循环的主要内容,如果未能解决你的问题,请参考以下文章
在 Flyway Java API 中使用 SQL 迁移文件
在 Flyway 中编写 Java 迁移时,有没有办法使用 HibernateTemplate/JPATemplate?