Spring Boot 中使用 Flyway 實作多資料庫
1.概述
在使用微服務或複雜的單體應用時,通常需要使用多個資料庫來處理不同的領域,例如使用者、產品等。 Spring Boot 為管理此類多資料庫設定提供了強大的支援。然而,管理多個資料庫的架構遷移可能比較棘手。
在本教程中,我們將探討如何將 Flyway 與 Spring Boot 集成,以便在單一應用程式中支援多個資料庫。我們將使用兩個獨立的 H2 記憶體資料庫(一個用於用戶,一個用於產品),並為每個資料庫分別使用 Flyway 進行遷移。
2. Maven配置
在深入研究之前,我們將透過Spring Initializr使用 Maven 設定一個簡單的 Spring Boot 應用程式。
首先,我們需要配置所需的依賴項。我們包含了用於 資料 JPA 、 Flyway 、 H2和測試的Spring Boot 啟動器。這些依賴項將允許我們使用 Flyway 管理模式版本,並使用 JPA 與資料庫層互動:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>9.22.3</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>3.2.3</version>
<scope>test</scope>
</dependency>
這些依賴關係對於啟用模式遷移、設定記憶體資料庫和編寫單元測試進行驗證至關重要。
3. 多資料庫配置
在本節中,我們將定義資料來源、Flyway Bean、JPA 配置以及支援多資料庫連線和模式遷移所需的所有元件。 Spring Boot 支援單一資料來源的自動配置,因此對於多資料庫,我們需要手動提供自訂配置。這包括為每個資料庫建立單獨的資料來源 Bean、實體管理器工廠和事務管理器。
我們還需要單獨配置 Flyway,以便每個資料庫都能執行其遷移腳本。這些配置確保職責清晰分離,並防止衝突。
3.1. 應用程式屬性
application.yml
定義了兩個不同的資料來源,一個用於userdb
,另一個用於productdb
。這些設定允許 Spring 分別識別並連接每個資料庫。**此外,我們將停用 Flyway 的預設自動配置,以防止其僅將遷移套用至主資料來源。**此設定確保我們完全控制遷移的執行方式和位置:
spring:
flyway:
enabled: false
userdb:
datasource:
url: jdbc:h2:mem:userdb;DB_CLOSE_DELAY=-1
username: sa
password:
driver-class-name: org.h2.Driver
productdb:
datasource:
url: jdbc:h2:mem:productdb;DB_CLOSE_DELAY=-1
username: sa
password:
driver-class-name: org.h2.Driver
3.2. 使用者實體
User
實體是一個簡單的 JPA 模型,用來表示userdb
資料庫中與使用者相關的資料。它包含兩個基本欄位: id
和name
,均以列的形式儲存:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
3.3. 產品實體
Product
實體用於建模儲存在productdb
資料庫中的產品相關資料。它由兩個主要字段組成: id
和title
。與User
實體類似,它使用標準 JPA 註解來定義持久化行為:
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
private Long id;
private String name;
}
兩個實體都支援所有 CRUD 操作,並與配置的相應資料來源無縫集成 執行 ORM 映射.
3.4. 儲存庫
儲存庫介面負責封裝存取資料來源所需的邏輯。對於每個實體( User
和Product,
我們定義一個單獨的儲存庫接口,該接口擴展了JpaRepository 接口。此擴充功能使 Spring Data JPA 能夠自動產生標準資料存取方法,例如findById
、 save
、 deleteById
和findAll
,而無需樣板程式碼:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface ProductRepository extends JpaRepository<Product, Long> {}
UserRepository
與userdb
交互,而ProductRepository
則根據各自的配置連接到productdb
。這些介面在保持程式碼簡潔、可讀以及與實際持久化邏輯解耦方面發揮著至關重要的作用。
3.5. 配置類
在多資料庫設定中,Spring Boot 不會自動管理多個資料來源或遷移腳本。因此,我們需要明確配置每個資料庫的連線、實體掃描、事務管理和 Flyway 遷移設定。
我們為每個資料庫userdb
和productdb
定義了單獨的配置類,以保持設定的模組化和可維護性。這種明確配置確保每個資料庫都能運行各自的遷移並管理各自的事務邊界,從而避免衝突或運行時歧義。
這是userdb
的設定:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.baeldung.repository.user",
entityManagerFactoryRef = "userEntityManagerFactory",
transactionManagerRef = "userTransactionManager"
)
public class UserDbConfig {
@Bean
@Primary
public DataSource userDataSource() {
return DataSourceBuilder.create()
.url("jdbc:h2:mem:userdb")
.username("sa")
.password("")
.driverClassName("org.h2.Driver")
.build();
}
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean userEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(userDataSource())
.packages("com.baeldung.entity")
.persistenceUnit("userPU")
.properties(Map.of("hibernate.hbm2ddl.auto", "none"))
.build();
}
@Bean
@Primary
public PlatformTransactionManager userTransactionManager(
EntityManagerFactory userEntityManagerFactory) {
return new JpaTransactionManager(userEntityManagerFactory);
}
@PostConstruct
public void migrateUserDb() {
Flyway.configure()
.dataSource(userDataSource())
.locations("classpath:db/migration/userdb")
.load()
.migrate();
}
}
productdb, but
具有不同的套件和遷移路徑:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = "com.baeldung.repository.product",
entityManagerFactoryRef = "productEntityManagerFactory",
transactionManagerRef = "productTransactionManager"
)
public class ProductDbConfig {
@Bean
public DataSource productDataSource() {
return DataSourceBuilder.create()
.url("jdbc:h2:mem:productdb")
.username("sa")
.password("")
.driverClassName("org.h2.Driver")
.build();
}
@Bean
public LocalContainerEntityManagerFactoryBean productEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(productDataSource())
.packages("com.baeldung.entity")
.persistenceUnit("productPU")
.properties(Map.of("hibernate.hbm2ddl.auto", "none"))
.build();
}
@Bean
public PlatformTransactionManager productTransactionManager(
EntityManagerFactory productEntityManagerFactory) {
return new JpaTransactionManager(productEntityManagerFactory);
}
@PostConstruct
public void migrateProductDb() {
Flyway.configure()
.dataSource(productDataSource())
.locations("classpath:db/migration/productdb")
.load();
.migrate();
}
}
3.6. SQL 遷移
Flyway 使用版本化 SQL 腳本以可預測和可重複的方式管理資料庫模式變更。在多資料庫設定中,我們將這些遷移腳本組織在單獨的目錄中,例如db/migration/userdb
和db/migration/productdb
,以防止版本衝突並確保模式之間的隔離:
以下配置針對userdb
資料庫:
-- V1__create_users_table.sql
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(255)
);
以下配置針對productdb
資料庫:
-- V1__create_products_table.sql
CREATE TABLE products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
這種結構對於避免錯誤至關重要,並有助於清晰地管理每個資料域的模式變化。
3.7. 單元測試
在這裡,我們將在多資料庫設定中驗證服務層邏輯和資料庫互動的正確性。此測試確保實體在各自的資料庫中正確保存和檢索。它還驗證了配置連接、Bean 初始化和 Flyway 遷移。
@Test
void givenUsersAndProducts_whenSaved_thenFoundById() {
User user = new User();
user.setName("John");
userRepository.save(user);
Product product = new Product();
product.setName("Laptop");
productRepository.save(product);
assertTrue(userRepository.findById(user.getId()).isPresent());
assertTrue(productRepository.findById(product.getId()).isPresent());
}
此測試確保兩個資料庫獨立工作並正確保存資料。
4. 結論
在本教學中,我們探討如何在 Spring Boot 中設定兩個資料庫,分別管理每個資料庫的 Flyway 遷移,以及如何使用單元測試驗證設定。透過相應地修改資料來源屬性,可以輕鬆擴展此模式以支援其他資料庫或其他資料庫引擎,例如 PostgreSQL 或 MySQL。
在 Spring Boot 應用程式中管理多個資料庫可能頗具挑戰性,尤其是在涉及模式版本控制的情況下。透過為每個資料來源明確定義配置、儲存庫和 Flyway 設置,我們可以清晰地分離關注點並實現平滑遷移。這種方法不僅提高了可維護性,而且與企業應用程式中經常需要的模組化架構完美契合。
與往常一樣,這些範例的程式碼可在 GitHub 上找到。