Flyway Spring 使用 JPA 依赖项启动自动装配的 Bean
Flyway Spring Boot Autowired Beans with JPA Dependency
我正在使用 Flyway 5.0.5,我无法创建具有自动装配属性的 java (SpringJdbcMigration)...它们最终 null
.
我能找到的最接近的是这个问题:
答案提到它已在 Flyway 5 中修复,但链接已失效。
我错过了什么?
该功能尚未纳入 Flyway。 this issue 正在跟踪它。在撰写本文时,该问题已打开并分配给 5.1.0 里程碑。
由于我对 JPA 的依赖,我为此苦苦挣扎了很长时间。我将稍微编辑我的问题标题以反映这一点...
@Autowired
bean 从 ApplicationContext
实例化。我们可以创建一个不同的 bean ApplicationContextAware
并将其用于 "manually wire" 我们的 bean 以用于迁移。
可以找到一个非常干净的方法 here。不幸的是,这会在使用 JPA 时引发未捕获的异常(具体来说,ApplicationContext
为空)。幸运的是,我们可以通过使用 @DependsOn
注释解决这个问题,并在设置 ApplicationContext
后强制飞路到 运行。
首先我们需要上面 avehlies/spring-beans-flyway2
中的 SpringUtility
。
package com.mypackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@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
*/
public static <T> T getBean(final Class clazz) {
return (T) applicationContext.getBean(clazz);
}
/*
Return the application context if necessary for anything else
*/
public static ApplicationContext getContext() {
return applicationContext;
}
}
然后,为 springUtility
配置 flywayInitializer
和 @DependsOn
。我在这里扩展了 FlywayAutoConfiguration
希望保留自动配置功能。这似乎对我有用,除了在我的 gradle.build 文件中关闭 flyway 不再有效,所以我不得不添加 @Profile("!integration")
以防止它在我的测试期间 运行ning .除此之外,自动配置似乎对我有用,但不可否认我只有 运行 一次迁移。如果我错了,希望有人能纠正我。
package com.mypackage;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.DependsOn;
import com.mypackage.SpringUtility;
@Configuration
@Profile("!integration")
class MyFlywayConfiguration extends FlywayConfiguration {
@Primary
@Bean(name = "flywayInitializer")
@DependsOn("springUtility")
public FlywayMigrationInitializer flywayInitializer(Flyway flyway){
return super.flywayInitializer(flyway);
//return new FlywayMigrationInitializer(flyway, null);
}
}
为了完成示例,这里有一个迁移:
package db.migration;
import org.flywaydb.core.api.migration.spring.BaseSpringJdbcMigration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.mypackage.repository.AccountRepository;
import com.mypackage.domain.Account;
import com.mypackage.SpringUtility;
import java.util.List;
public class V2__account_name_ucase_firstname extends BaseSpringJdbcMigration {
private AccountRepository accountRepository = SpringUtility.getBean(AccountRepository.class);
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
List<Account> accounts = accountRepository.findAll();
for (Account account : accounts) {
String firstName = account.getFirstName();
account.setFirstName(firstName.substring(0, 1).toUpperCase() + firstName.substring(1));
account = accountRepository.save(account);
}
}
}
感谢 github 上的 avehlies、堆栈溢出上的 Andy Wilkinson 和 github 上的 OldIMP 一路帮助我。
如果您使用的是更新版本的 Flyway,则扩展 BaseJavaMigration
而不是 BaseSpringJdbcMigration
,因为后者已被弃用。另外,请看下面 two comments 用户 Wim Deblauwe 的文章。
如果您使用的是 deltaspike,则可以使用 BeanProvider 获取对您的 DAO 的引用。
更改您的 DAO 代码:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
然后在你的迁移方法中:
UserDao userdao = UserDao.getInstance();
这就是你的参考资料。
(引用自:)
@mararn1618 提供的更新答案似乎在官方文档中记录不足,因此我将在此处提供一个工作设置。感谢@mararn1618 在这个方向上的指导。
免责声明,它是用 Kotlin 编写的:)
首先你需要一个用于加载迁移的配置 类,在 Spring 引导(也许 Spring)你需要一个 FlywayConfigurationCustomizer
的实现或一个设置FlywayAutoConfiguration.FlywayConfiguration
个。只测试了第一个,但两者都应该有效
配置一,测试
import org.flywaydb.core.api.configuration.FluentConfiguration
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
@Component
class MyFlywayConfiguration @Autowired constructor(
val applicationContext: ApplicationContext
) : FlywayConfigurationCustomizer {
override fun customize(configuration: FluentConfiguration?) {
val migrationBeans = applicationContext.getBeansOfType(JavaMigration::class.java)
val migrationBeansAsArray = migrationBeans.values.toTypedArray()
configuration?.javaMigrations(*migrationBeansAsArray)
}
}
配置选项 B,未经测试,但应该也可以工作
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class MyFlywayConfiguration : FlywayAutoConfiguration.FlywayConfiguration() {
@Bean
fun flywayConfigurationCustomizer(applicationContext: ApplicationContext): FlywayConfigurationCustomizer {
return FlywayConfigurationCustomizer { flyway ->
val p = applicationContext.getBeansOfType(JavaMigration::class.java)
val v = p.values.toTypedArray()
flyway.javaMigrations(*v)
}
}
}
有了它,您几乎可以像编写任何其他 Spring bean 一样编写迁移:
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class V7_1__MyMigration @Autowired constructor(
) : BaseJavaMigration() {
override fun migrate(context: Context?) {
TODO("go crazy, mate, now you can import beans, but be aware of circular dependencies")
}
}
旁注:
- 小心循环依赖,你的迁移很可能不依赖于存储库(这也是有道理的,毕竟你正在准备它们)
- 确保您的迁移位于 Spring 扫描 类 的位置。所以如果你想把它们放在命名空间
db/migrations
中,你需要确保 Spring 扫描那个位置
- 我没有测试过,但可能应该谨慎混合这些迁移的路径和 Flyway 扫描迁移的位置
当前的 flyway 6.5.5 版本已发布并从 6.0.0 返回我相信提供了对 spring beans 的支持。
您可以直接将 spring bean 自动装配到基于 Java 的迁移中(使用 @autowired),但直觉是您的迁移 class 也应该由 Spring 管理以解决依赖关系。
有一个很酷很简单的方法,通过覆盖 Flyway 的默认行为,查看 https://reflectoring.io/database-migration-spring-boot-flyway/
这篇文章用代码片段清楚地回答了你的问题。
我正在使用 Flyway 5.0.5,我无法创建具有自动装配属性的 java (SpringJdbcMigration)...它们最终 null
.
我能找到的最接近的是这个问题:
答案提到它已在 Flyway 5 中修复,但链接已失效。
我错过了什么?
该功能尚未纳入 Flyway。 this issue 正在跟踪它。在撰写本文时,该问题已打开并分配给 5.1.0 里程碑。
由于我对 JPA 的依赖,我为此苦苦挣扎了很长时间。我将稍微编辑我的问题标题以反映这一点...
@Autowired
bean 从 ApplicationContext
实例化。我们可以创建一个不同的 bean ApplicationContextAware
并将其用于 "manually wire" 我们的 bean 以用于迁移。
可以找到一个非常干净的方法 here。不幸的是,这会在使用 JPA 时引发未捕获的异常(具体来说,ApplicationContext
为空)。幸运的是,我们可以通过使用 @DependsOn
注释解决这个问题,并在设置 ApplicationContext
后强制飞路到 运行。
首先我们需要上面 avehlies/spring-beans-flyway2
中的 SpringUtility
。
package com.mypackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@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
*/
public static <T> T getBean(final Class clazz) {
return (T) applicationContext.getBean(clazz);
}
/*
Return the application context if necessary for anything else
*/
public static ApplicationContext getContext() {
return applicationContext;
}
}
然后,为 springUtility
配置 flywayInitializer
和 @DependsOn
。我在这里扩展了 FlywayAutoConfiguration
希望保留自动配置功能。这似乎对我有用,除了在我的 gradle.build 文件中关闭 flyway 不再有效,所以我不得不添加 @Profile("!integration")
以防止它在我的测试期间 运行ning .除此之外,自动配置似乎对我有用,但不可否认我只有 运行 一次迁移。如果我错了,希望有人能纠正我。
package com.mypackage;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.DependsOn;
import com.mypackage.SpringUtility;
@Configuration
@Profile("!integration")
class MyFlywayConfiguration extends FlywayConfiguration {
@Primary
@Bean(name = "flywayInitializer")
@DependsOn("springUtility")
public FlywayMigrationInitializer flywayInitializer(Flyway flyway){
return super.flywayInitializer(flyway);
//return new FlywayMigrationInitializer(flyway, null);
}
}
为了完成示例,这里有一个迁移:
package db.migration;
import org.flywaydb.core.api.migration.spring.BaseSpringJdbcMigration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.mypackage.repository.AccountRepository;
import com.mypackage.domain.Account;
import com.mypackage.SpringUtility;
import java.util.List;
public class V2__account_name_ucase_firstname extends BaseSpringJdbcMigration {
private AccountRepository accountRepository = SpringUtility.getBean(AccountRepository.class);
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
List<Account> accounts = accountRepository.findAll();
for (Account account : accounts) {
String firstName = account.getFirstName();
account.setFirstName(firstName.substring(0, 1).toUpperCase() + firstName.substring(1));
account = accountRepository.save(account);
}
}
}
感谢 github 上的 avehlies、堆栈溢出上的 Andy Wilkinson 和 github 上的 OldIMP 一路帮助我。
如果您使用的是更新版本的 Flyway,则扩展 BaseJavaMigration
而不是 BaseSpringJdbcMigration
,因为后者已被弃用。另外,请看下面 two comments 用户 Wim Deblauwe 的文章。
如果您使用的是 deltaspike,则可以使用 BeanProvider 获取对您的 DAO 的引用。
更改您的 DAO 代码:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
然后在你的迁移方法中:
UserDao userdao = UserDao.getInstance();
这就是你的参考资料。
(引用自:
@mararn1618 提供的更新答案似乎在官方文档中记录不足,因此我将在此处提供一个工作设置。感谢@mararn1618 在这个方向上的指导。
免责声明,它是用 Kotlin 编写的:)
首先你需要一个用于加载迁移的配置 类,在 Spring 引导(也许 Spring)你需要一个 FlywayConfigurationCustomizer
的实现或一个设置FlywayAutoConfiguration.FlywayConfiguration
个。只测试了第一个,但两者都应该有效
配置一,测试
import org.flywaydb.core.api.configuration.FluentConfiguration
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
@Component
class MyFlywayConfiguration @Autowired constructor(
val applicationContext: ApplicationContext
) : FlywayConfigurationCustomizer {
override fun customize(configuration: FluentConfiguration?) {
val migrationBeans = applicationContext.getBeansOfType(JavaMigration::class.java)
val migrationBeansAsArray = migrationBeans.values.toTypedArray()
configuration?.javaMigrations(*migrationBeansAsArray)
}
}
配置选项 B,未经测试,但应该也可以工作
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class MyFlywayConfiguration : FlywayAutoConfiguration.FlywayConfiguration() {
@Bean
fun flywayConfigurationCustomizer(applicationContext: ApplicationContext): FlywayConfigurationCustomizer {
return FlywayConfigurationCustomizer { flyway ->
val p = applicationContext.getBeansOfType(JavaMigration::class.java)
val v = p.values.toTypedArray()
flyway.javaMigrations(*v)
}
}
}
有了它,您几乎可以像编写任何其他 Spring bean 一样编写迁移:
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
@Component
class V7_1__MyMigration @Autowired constructor(
) : BaseJavaMigration() {
override fun migrate(context: Context?) {
TODO("go crazy, mate, now you can import beans, but be aware of circular dependencies")
}
}
旁注:
- 小心循环依赖,你的迁移很可能不依赖于存储库(这也是有道理的,毕竟你正在准备它们)
- 确保您的迁移位于 Spring 扫描 类 的位置。所以如果你想把它们放在命名空间
db/migrations
中,你需要确保 Spring 扫描那个位置 - 我没有测试过,但可能应该谨慎混合这些迁移的路径和 Flyway 扫描迁移的位置
当前的 flyway 6.5.5 版本已发布并从 6.0.0 返回我相信提供了对 spring beans 的支持。 您可以直接将 spring bean 自动装配到基于 Java 的迁移中(使用 @autowired),但直觉是您的迁移 class 也应该由 Spring 管理以解决依赖关系。 有一个很酷很简单的方法,通过覆盖 Flyway 的默认行为,查看 https://reflectoring.io/database-migration-spring-boot-flyway/ 这篇文章用代码片段清楚地回答了你的问题。