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

多模块 Gradle 项目中的 Flyway 迁移(干净架构)

Jan 19, 2025 am 08:05 AM

使用 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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Java的类负载机制如何起作用,包括不同的类载荷及其委托模型? Java的类负载机制如何起作用,包括不同的类载荷及其委托模型? Mar 17, 2025 pm 05:35 PM

Java的类上载涉及使用带有引导,扩展程序和应用程序类负载器的分层系统加载,链接和初始化类。父代授权模型确保首先加载核心类别,从而影响自定义类LOA

如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存? 如何使用咖啡因或Guava Cache等库在Java应用程序中实现多层缓存? Mar 17, 2025 pm 05:44 PM

本文讨论了使用咖啡因和Guava缓存在Java中实施多层缓存以提高应用程序性能。它涵盖设置,集成和绩效优势,以及配置和驱逐政策管理最佳PRA

如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射? 如何将JPA(Java持久性API)用于具有高级功能(例如缓存和懒惰加载)的对象相关映射? Mar 17, 2025 pm 05:43 PM

本文讨论了使用JPA进行对象相关映射,并具有高级功能,例如缓存和懒惰加载。它涵盖了设置,实体映射和优化性能的最佳实践,同时突出潜在的陷阱。[159个字符]

如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案? 如何将Maven或Gradle用于高级Java项目管理,构建自动化和依赖性解决方案? Mar 17, 2025 pm 05:46 PM

本文讨论了使用Maven和Gradle进行Java项目管理,构建自动化和依赖性解决方案,以比较其方法和优化策略。

如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)? 如何使用适当的版本控制和依赖项管理创建和使用自定义Java库(JAR文件)? Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之类的工具讨论了具有适当的版本控制和依赖关系管理的自定义Java库(JAR文件)的创建和使用。

See all articles