資料庫遷移是軟體開發的重要方面,特別是在持續整合和交付 (CI/CD) 為標準實踐的環境中。隨著應用程式的成長和發展,它所依賴的資料庫模式也必須如此。手動管理這些架構變更可能會導致錯誤並消耗大量時間。
Flyway 登場,這是一款專為簡化資料庫遷移而客製化的寶貴開源工具。 Flyway 為您的資料庫引入了版本控制,使您能夠安全可靠地遷移架構。在本文中,我們將探索如何使用 Flyway 在多模組 gragle java 專案中自動化資料庫遷移,確保管理資料庫變更成為一個簡化的、防錯的過程。
更多有關飛行路線的詳細資訊
雖然一些較小的專案或整體應用程式可能僅使用一個建置檔案和統一的來源結構進行管理,但較大的專案經常被組織成多個相互依賴的模組。術語「相互依賴」是這裡的關鍵,強調了透過單一建置過程連接這些模組的需要。
Gradle 以其多專案建置功能(通常稱為多模組專案)來滿足這種設定。在 Gradle 的術語中,這些模組稱為子項目。
多專案建置是圍繞一個根專案建構的,並且可以包含其下的多個子專案。
目錄結構應如下圖所示:
├── .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 的子模組結構來組織程式碼庫。以下是如何將 Clean Architecture 與 Gradle 子模組結合:
乾淨的架構層:
核心:
外在:
網頁:
├── .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中文網其他相關文章!