搜索
首页Javajava教程多模块 Gradle 项目中的 Flyway 迁移(干净架构)

使用 Flyway 在 Java 中自动执行数据库迁移

数据库迁移是软件开发的一个重要方面,特别是在持续集成和交付 (CI/CD) 为标准实践的环境中。随着应用程序的增长和发展,它所依赖的数据库模式也必须如此。手动管理这些架构更改可能会导致错误并消耗大量时间。

Flyway 登场,这是一款专为简化数据库迁移而定制的宝贵开源工具。 Flyway 为您的数据库引入了版本控制,使您能够安全可靠地迁移架构。在本文中,我们将探索如何使用 Flyway 在多模块 gragle java 项目中自动化数据库迁移,确保管理数据库更改成为一个简化的、防错的过程。

有关飞行路线的更多详细信息

了解 Gradle 中的多项目构建

虽然一些较小的项目或整体应用程序可能仅使用一个构建文件和统一的源结构进行管理,但较大的项目经常被组织成多个相互依赖的模块。术语“相互依赖”是这里的关键,强调了通过单一构建过程连接这些模块的需要。

Gradle 以其多项目构建功能(通常称为多模块项目)来满足这种设置。在 Gradle 的术语中,这些模块称为子项目。

多项目构建是围绕一个根项目构建的,并且可以包含其下的多个子项目。

gradle project

目录结构应如下所示:

├── .gradle
│   └── ⋮
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
├── gradlew
├── gradlew.bat
├── settings.gradle.kts (1)
├── sub-project-1
│   └── build.gradle.kts (2) 
├── sub-project-2
│   └── build.gradle.kts (2) 
└── sub-project-3
    └── build.gradle.kts (2)

(1) settings.gradle.kts 文件应包含所有子项目。
(2) 每个子项目应该有自己的build.gradle.kts文件。

利用 Gradle 子模块实现简洁的架构

清洁架构是一种强调关注点分离的设计模式,使软件更易于维护和测试。在项目中实现此架构的实用方法之一是使用 Gradle 的子模块结构来组织代码库。以下是如何将 Clean Architecture 与 Gradle 子模块结合起来:

干净的架构层:
核心:

  • 包含业务逻辑、领域模型和应用程序规则。 不依赖于外部或网络。
  • 应尽可能独立于特定于框架的实现。

外部:

  • 处理外部操作或集成,例如数据库迁移或第三方服务交互。
  • 业务逻辑可能依赖于 Core,但不应依赖于 Web。

网页:

  • 入口点,公开 REST API 并处理 HTTP 请求。
  • 业务逻辑依赖于核心,集成可能依赖于外部。
├── .gradle
│   └── ⋮
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
├── gradlew
├── gradlew.bat
├── settings.gradle.kts (1)
├── sub-project-1
│   └── build.gradle.kts (2) 
├── sub-project-2
│   └── build.gradle.kts (2) 
└── sub-project-3
    └── build.gradle.kts (2)

第 1 步: 创建一个基于 Java 的 Gradle 项目并将其命名为“SchoolStaff”。

第 2 步: 转到 Spring Initializr 并生成一个名为 Web.

的 REST API 项目

第 3 步: 创建一个基于 Java 的 Gradle 项目并将其命名为 External.

第 4 步: 创建一个基于 Java 的 Gradle 项目并将其命名为 Core.

根目录 build.gradle.kts

SchoolStaff/
├── Core/
│   ├── src/
│   │   └── main/
│   │       ├── java/         # Business logic and domain objects
│   │       └── resources/    # Core-specific resources (if any)
│   └── build.gradle.kts
├── External/
│   ├── src/
│   │   └── main/
│   │       ├── java/         # External integration code
│   │       └── resources/    # db/migration and other external resources
│   └── build.gradle.kts
├── Web/
│   ├── src/
│   │   └── main/
│   │       ├── java/         # REST controllers and entry-point logic
│   │       └── resources/    # Application-specific configuration
│   └── build.gradle.kts
├── build.gradle.kts          # Root Gradle build
└── settings.gradle.kts       # Project module settings

settings.gradle.kts

plugins {
    id("java")
}

allprojects {
    group = "school.staff"
    version = "1.0.0"

    repositories {
        mavenLocal()
        mavenCentral()
    }
}

subprojects {
    apply(plugin = "java")

    dependencies {
        testImplementation(platform("org.junit:junit-bom:5.10.0"))
        testImplementation("org.junit.jupiter:junit-jupiter")
    }

    tasks.test {
        useJUnitPlatform()
    }
}

“Web”项目所需的依赖项。

rootProject.name = "SchoolStaff"

include("Core", "External", "Web")

“核心”项目所需的依赖项。

dependencies {
    implementation(project(":Core"))
    implementation(project(":External"))
}

“外部”项目所需的依赖项。

dependencies {
    runtimeOnly(project(":External"))
}

我们使用以下插件进行 Flyway 迁移:

import java.sql.DriverManager
import java.util.Properties
// Function to load properties based on the environment
fun loadProperties(env: String): Properties {
    val properties = Properties()
    val propsFile = file("../web/src/main/resources/application-$env.properties")

    if (propsFile.exists()) {
        propsFile.inputStream().use { properties.load(it) }
    } else {
        throw GradleException("Properties file for environment '$env' not found: ${propsFile.absolutePath}")
    }

    return properties
}
// Set the environment (default is 'dev' if no argument is passed)
val env = project.findProperty("env")?.toString() ?: "dev"

// Load properties for the chosen environment
val dbProps = loadProperties(env)
buildscript {
    dependencies {
        classpath("org.flywaydb:flyway-database-postgresql:11.1.0") // This is required for the flyway plugin to work on the migration, otherwise it will throw an error as No Database found
        classpath("org.postgresql:postgresql:42.7.4")
    }
}
plugins {
    id("java-library")
    id("org.flywaydb.flyway") version "11.0.1"
}

group = "school.staff"
version = "unspecified"

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-jpa:3.4.0")
    implementation("org.postgresql:postgresql:42.7.4")
    implementation("org.flywaydb:flyway-core:11.0.1")
    implementation("org.flywaydb:flyway-database-postgresql:11.0.1")
    implementation("org.flywaydb:flyway-gradle-plugin:11.0.1")

    implementation (project(":Core"))
    testImplementation(platform("org.junit:junit-bom:5.10.0"))
    testImplementation("org.junit.jupiter:junit-jupiter")
}

tasks.test {
    useJUnitPlatform()
}

// Task to create the database if it doesn't exist
tasks.register("createDatabase") {
    doLast {
        val dbUrl = dbProps["spring.datasource.url"] as String
        val dbUsername = dbProps["spring.datasource.username"] as String
        val dbPassword = dbProps["spring.datasource.password"] as String

        // Extract the base URL and database name
        val baseDbUrl = dbUrl.substringBeforeLast("/")+ "/"
        val dbName = dbUrl.substringAfterLast("/")

        // Connect to the PostgreSQL server (without the specific database)
        DriverManager.getConnection(baseDbUrl, dbUsername, dbPassword).use { connection ->
            val stmt = connection.createStatement()
            val resultSet = stmt.executeQuery("SELECT 1 FROM pg_database WHERE datname = '$dbName'")
            if (!resultSet.next()) {
                println("Database '$dbName' does not exist. Creating it...")
                stmt.executeUpdate("CREATE DATABASE \"$dbName\"")
                println("Database '$dbName' created successfully.")
            } else {
                println("Database '$dbName' already exists.")
            }
        }
    }
}

flyway {
    url = dbProps["spring.datasource.url"] as String
    user = dbProps["spring.datasource.username"] as String
    password = dbProps["spring.datasource.password"] as String
    locations = arrayOf("classpath:db/migration")
    baselineOnMigrate = true
}

 //Ensure classes are built before migration
tasks.named("flywayMigrate").configure {
    dependsOn(tasks.named("createDatabase"))
    dependsOn(tasks.named("classes"))
}

这种方法非常适合生产环境,因为它确保了受控且可靠的迁移。我们不会在每次应用程序启动时自动运行迁移,而是仅在必要时执行迁移,从而提供更大的灵活性和控制力。

我们还利用 Spring 应用程序中的 application.properties 文件来管理数据库连接和凭据。 BaselineOnMigrate = true 设置可确保将初始迁移用作未来迁移的基线。

 plugins {
    id("org.flywaydb.flyway") version "11.0.1"
}

我们可以使用 JPA Buddy 生成外部项目的 resources/db/migration 目录中的所有迁移文件。

V1__Initial_Migration

flyway {
    url = dbProps["spring.datasource.url"] as String
    user = dbProps["spring.datasource.username"] as String
    password = dbProps["spring.datasource.password"] as String
    locations = arrayOf("classpath:db/migration")
    baselineOnMigrate = true
}

从根项目中,我们可以使用以下命令执行 Flyway 迁移:

CREATE TABLE _user
(
    id                 UUID NOT NULL,
    created_by         UUID,
    created_date       TIMESTAMP WITH TIME ZONE,
    last_modified_by   UUID,
    last_modified_date TIMESTAMP WITH TIME ZONE,
    first_name         VARCHAR(255),
    last_name          VARCHAR(255),
    email              VARCHAR(255),
    password           VARCHAR(255),
    tenant_id          UUID,
    CONSTRAINT pk__user PRIMARY KEY (id)
);

这会将所有迁移文件应用到数据库。

结论

我们探索了如何在 Gradle 多模块项目中使用 Flyway 自动化数据库迁移,这对于在 CI/CD 环境中维护架构一致性至关重要。

我们还介绍了 Gradle 如何支持多项目构建,将复杂的项目组织成可管理的子项目,每个子项目都有自己的构建配置,统一在根构建脚本下。

最后,我们将 Clean Architecture 与 Gradle 模块结合起来,将项目构建为核心层、外部层和 Web 层,促进关注点和依赖管理的清晰分离。

这些实践增强了模块化、自动化和可维护性,为可扩展、无错误的软件开发奠定了基础。

以上是多模块 Gradle 项目中的 Flyway 迁移(干净架构)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JVM性能与其他语言JVM性能与其他语言May 14, 2025 am 12:16 AM

JVM'SperformanceIsCompetitiveWithOtherRuntimes,operingabalanceOfspeed,安全性和生产性。1)JVMUSESJITCOMPILATIONFORDYNAMICOPTIMIZAIZATIONS.2)c提供NativePernativePerformanceButlanceButlactsjvm'ssafetyFeatures.3)

Java平台独立性:使用示例Java平台独立性:使用示例May 14, 2025 am 12:14 AM

JavaachievesPlatFormIndependencEthroughTheJavavIrtualMachine(JVM),允许CodeTorunonAnyPlatFormWithAjvm.1)codeisscompiledIntobytecode,notmachine-specificodificcode.2)bytecodeisisteredbytheybytheybytheybythejvm,enablingcross-platerssectectectectectross-eenablingcrossectectectectectection.2)

JVM架构:深入研究Java虚拟机JVM架构:深入研究Java虚拟机May 14, 2025 am 12:12 AM

TheJVMisanabstractcomputingmachinecrucialforrunningJavaprogramsduetoitsplatform-independentarchitecture.Itincludes:1)ClassLoaderforloadingclasses,2)RuntimeDataAreafordatastorage,3)ExecutionEnginewithInterpreter,JITCompiler,andGarbageCollectorforbytec

JVM:JVM与操作系统有关吗?JVM:JVM与操作系统有关吗?May 14, 2025 am 12:11 AM

JVMhasacloserelationshipwiththeOSasittranslatesJavabytecodeintomachine-specificinstructions,managesmemory,andhandlesgarbagecollection.ThisrelationshipallowsJavatorunonvariousOSenvironments,butitalsopresentschallengeslikedifferentJVMbehaviorsandOS-spe

Java:写一次,在任何地方跑步(WORA) - 深入了解平台独立性Java:写一次,在任何地方跑步(WORA) - 深入了解平台独立性May 14, 2025 am 12:05 AM

Java实现“一次编写,到处运行”通过编译成字节码并在Java虚拟机(JVM)上运行。1)编写Java代码并编译成字节码。2)字节码在任何安装了JVM的平台上运行。3)使用Java原生接口(JNI)处理平台特定功能。尽管存在挑战,如JVM一致性和平台特定库的使用,但WORA大大提高了开发效率和部署灵活性。

Java平台独立性:与不同的操作系统的兼容性Java平台独立性:与不同的操作系统的兼容性May 13, 2025 am 12:11 AM

JavaachievesPlatFormIndependencethroughTheJavavIrtualMachine(JVM),允许Codetorunondifferentoperatingsystemsswithoutmodification.thejvmcompilesjavacodeintoplatform-interploplatform-interpectentbybyteentbytybyteentbybytecode,whatittheninternterninterpretsandectectececutesoneonthepecificos,atrafficteyos,Afferctinginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginginging

什么功能使Java仍然强大什么功能使Java仍然强大May 13, 2025 am 12:05 AM

JavaispoperfulduetoitsplatFormitiondence,对象与偏见,RichstandardLibrary,PerformanceCapabilities和StrongsecurityFeatures.1)Platform-dimplighandependectionceallowsenceallowsenceallowsenceallowsencationSapplicationStornanyDevicesupportingJava.2)

顶级Java功能:开发人员的综合指南顶级Java功能:开发人员的综合指南May 13, 2025 am 12:04 AM

Java的顶级功能包括:1)面向对象编程,支持多态性,提升代码的灵活性和可维护性;2)异常处理机制,通过try-catch-finally块提高代码的鲁棒性;3)垃圾回收,简化内存管理;4)泛型,增强类型安全性;5)ambda表达式和函数式编程,使代码更简洁和表达性强;6)丰富的标准库,提供优化过的数据结构和算法。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具