使用 Flyway 和 Spring Boot 迁移具有不同生命周期的多个模式

Mat*_*bin 9 flyway spring-boot

迁飞路线常见问题分开3箱子多种模式的:

  1. 多个相同的模式
  2. 模式不同,但具有相同的生命周期
  3. 模式有一个独特的生命周期,或者必须是自主的和干净的分离

我们正在使用 Maven 构建一个多模块 Spring Boot 4.5.9 项目。每个模块都是完全独立的,并且有自己的数据库模式。所有模式都驻留在一个数据库中,因此只有一个 Spring 数据源。

因为模块是独立的,我们想分别管理它们各自的模式迁移,所以上面的选项(3)是最合适的。

但是,我找不到按照 Flyway FAQ 建议的方式配置 Spring Boot 的 Flyway 集成的方法:

使用多个 Flyway 实例。每个实例管理自己的模式并引用自己的模式历史表。将每个架构的迁移放在不同的位置。

理想情况下,每个模块都有自己的db/migration文件夹,其中包含自己的迁移 SQL 脚本。每个模块脚本的版本应该独立于其他模块中的脚本版本,并且每个模块的迁移历史应该存储在该模块模式的表中。

如果我将迁移脚本放在每个模块的resources/db/migration文件夹中,flyway 会检测到它们,然后抱怨:

org.flywaydb.core.api.FlywayException: Found more than one migration with version 0
Run Code Online (Sandbox Code Playgroud)

有人知道如何完成所需的设置吗?

PS 所有这一切的最终目标是能够(有一天,当系统扩展时)将这些模块拉入单独的服务中,而无需经历地狱将数据库分解为多个部分。

ams*_*ams 14

这是一个很长的答案,有两种有效的方法。

  • 基于约定的版本控制,无序执行
  • 多种模式

基于约定的方法

实现此目的的一种方法是遵循为每个模块分配版本号的约定,然后让所有模块迁移成为主要版本的次要版本。例如,假设您具有以下模块结构。

  • config包含常见 spring boot 配置的模块所有其他模块都继承自该模块。这是将保留 application.yml 的模块
  • user包含用户注册模块的模块,user取决于config
  • email包含在后台发送电子邮件的代码的模块,email取决于config

用户模块将有一个db/migration文件夹,其中包含以下文件。

  • V2.001__create_users_schema.sql
  • V2.002__create_account_tables.sql
  • V2.003__create_x_tables.sql

email模块将有一个db/migration文件夹,其中包含以下文件

  • V3.001__create_email_schema.sql
  • V3.002__create_outbox_table.sql

通过上述版本控制约定,您可以随时返回并添加新的模块特定迁移。例如,应用上述迁移后,您可以添加一个V2.004__create_y_table.sql,并且 Flyway 将填充V2.003和之间的迁移V3.0001

您需要配置 Flyway 以允许无序迁移,否则您将收到错误。在启动时可以设置。

spring:
  flyway:
    out-of-order: true
Run Code Online (Sandbox Code Playgroud)

关键是每个模块的第一个迁移文件首先发出一条CREATE SCHEMA语句,然后后续迁移在所有 DDL 语句或对象引用中包含架构名称。

例如V2.001__create_users_schema.sql包含

CREATE SCHEMA users;
Run Code Online (Sandbox Code Playgroud)

V2.002__create_account_tables.sql包含

CREATE TABLE users.login(
    username text
);
Run Code Online (Sandbox Code Playgroud)

请注意,传递给 的名称包含架构名称CREATE TABLEusers.login

通过为每个模块使用单独的模式,将来可以更轻松地将模块及其数据库模式提取到单独的二进制文件中。由于 Spring Boot 使用单个数据库连接池,因此您必须有 1 个数据库用户可以访问所有模块的架构。这就需要保持警惕,确保以下事情不会意外发生。

  • 编写引用多个模式的视图、查询、存储过程
  • 让一个模块将数据插入另一模块的架构中
  • @Transactional不同模块中的方法相互调用。这是最容易搞砸的,因为模块通常会互相调用。您可以尝试通过两种方式解决此问题。选项 1所有 @Service 类均受包保护,模块仅通过 HTTP 相互调用。选项 2所有使用的 @Service 类方法都需要新的传输传播@Transactional(propagation = Propagation.REQUIRES_NEW)。如果目标是最终提取到微服务架构,我认为选项 1 更好。

这种方法适用于 PostgreSQL,不确定它与其他数据库的配合情况如何,并且基于常见问题解答https://flywaydb.org/documentation/faq#hot-fixes

多个 Flyway 实例

FlywayMigrationStrategySpringBoot在执行迁移时会查找类型的 bean 。您可以实现此接口,忽略应用程序级别迁移并创建多个特定于模块的迁移,下面的代码有效。

import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.stereotype.Component;

@Component
public class MultiModuleFlywayMigrationStrategy implements FlywayMigrationStrategy {


  @Override
  public void migrate(Flyway flyway) {
    var  dataSource = flyway.getConfiguration().getDataSource();
    Flyway testModule = Flyway.configure()
        .schemas("test")
        .locations("db/test")
        .dataSource(dataSource).load();

    Flyway ratingsModule = Flyway.configure()
        .schemas("rating")
        .locations("db/ratings")
        .dataSource(dataSource).load();

    // don't call flyway.migrate() since we don't want any migrations in db/migration
    testModule.migrate();
    ratingsModule.migrate();

  }
}
Run Code Online (Sandbox Code Playgroud)