資料庫初始化
SQL 資料庫可以透過不同的方式進行初始化,具體取決於你的技術棧。當然,如果資料庫是一個獨立的程序,你也可以手動進行初始化。建議僅使用一種機制來生成 schema。
使用 Hibernate 初始化資料庫
你可以設定 spring.jpa.hibernate.ddl-auto
來控制 Hibernate 的資料庫初始化。支援的值包括 none
、validate
、update
、create
和 create-drop
。Spring Boot 會根據你是否使用嵌入式資料庫為你選擇一個預設值。嵌入式資料庫透過檢視 Connection
型別和 JDBC URL 來識別。hsqldb
、h2
或 derby
是嵌入式資料庫,其他則不是。如果識別出嵌入式資料庫且未檢測到 schema 管理器(Flyway 或 Liquibase),則 ddl-auto
預設值為 create-drop
。在所有其他情況下,預設值為 none
。
從記憶體資料庫切換到“真實”資料庫時要小心,不要對新平臺中表和資料的存在做出假設。你必須顯式設定 ddl-auto
或使用其他機制之一來初始化資料庫。
你可以透過啟用 org.hibernate.SQL 日誌記錄器來輸出 schema 建立過程。如果你啟用了除錯模式,這將自動為你完成。 |
此外,如果在 Hibernate 從頭開始建立 schema(即 ddl-auto
屬性設定為 create
或 create-drop
)時,classpath 根目錄下的 import.sql
檔案會在啟動時執行。如果你小心謹慎,這對於演示和測試可能很有用,但在生產環境中,你可能不希望它出現在 classpath 中。這是 Hibernate 的一個特性(與 Spring 無關)。
使用基本 SQL 指令碼初始化資料庫
Spring Boot 可以自動建立你的 JDBC DataSource
或 R2DBC ConnectionFactory
的 schema(DDL 指令碼)並初始化其資料(DML 指令碼)。
預設情況下,它從 optional:classpath*:schema.sql
載入 schema 指令碼,從 optional:classpath*:data.sql
載入資料指令碼。這些 schema 和資料指令碼的位置可以分別使用 spring.sql.init.schema-locations
和 spring.sql.init.data-locations
進行定製。optional:
字首表示即使檔案不存在,應用也會啟動。要讓應用在檔案不存在時啟動失敗,請移除 optional:
字首。
此外,Spring Boot 會處理 optional:classpath*:schema-${platform}.sql
和 optional:classpath*:data-${platform}.sql
檔案(如果存在),其中 ${platform}
是 spring.sql.init.platform
的值。這允許你在必要時切換到特定於資料庫的指令碼。例如,你可以選擇將其設定為資料庫的供應商名稱(如 hsqldb
、h2
、oracle
、mysql
、postgresql
等等)。
預設情況下,SQL 資料庫初始化僅在使用嵌入式記憶體資料庫時執行。要始終初始化 SQL 資料庫,無論其型別如何,請將 spring.sql.init.mode
設定為 always
。類似地,要停用初始化,請將 spring.sql.init.mode
設定為 never
。預設情況下,Spring Boot 啟用其基於指令碼的資料庫初始化器的快速失敗特性。這意味著,如果指令碼導致異常,應用將無法啟動。你可以透過設定 spring.sql.init.continue-on-error
來調整此行為。
預設情況下,基於指令碼的 DataSource
初始化是在建立任何 JPA EntityManagerFactory
beans 之前執行的。schema.sql
可用於建立 JPA 管理實體的 schema,data.sql
可用於填充資料。雖然我們不建議使用多種資料來源初始化技術,但如果你希望基於指令碼的 DataSource
初始化能夠基於 Hibernate 執行的 schema 建立進行,請將 spring.jpa.defer-datasource-initialization
設定為 true
。這將把資料來源初始化推遲到任何 EntityManagerFactory
beans 建立和初始化之後。然後,schema.sql
可用於對 Hibernate 執行的任何 schema 建立進行補充,而 data.sql
可用於填充資料。
初始化指令碼支援 -- 作為單行註釋,支援 /* */ 作為塊註釋。不支援其他註釋格式。 |
如果你正在使用 更高階的資料庫遷移工具,例如 Flyway 或 Liquibase,你應該單獨使用它們來建立和初始化 schema。不建議將基本的 schema.sql
和 data.sql
指令碼與 Flyway 或 Liquibase 一起使用,未來版本將移除對此的支援。
初始化 Spring Batch 資料庫
如果你使用 Spring Batch,它預裝了適用於大多數流行資料庫平臺的 SQL 初始化指令碼。Spring Boot 可以檢測你的資料庫型別並在啟動時執行這些指令碼。如果你使用嵌入式資料庫,這會預設發生。你也可以為任何資料庫型別啟用它,如以下示例所示
-
屬性
-
YAML
spring.batch.jdbc.initialize-schema=always
spring:
batch:
jdbc:
initialize-schema: "always"
你也可以透過將 spring.batch.jdbc.initialize-schema
設定為 never
來顯式關閉初始化。
使用更高階的資料庫遷移工具
在啟動時執行 Flyway 資料庫遷移
要在啟動時自動執行 Flyway 資料庫遷移,請將相應的 Flyway 模組新增到你的 classpath 中。記憶體資料庫和檔案資料庫由 org.flywaydb:flyway-core
支援。否則,需要特定於資料庫的模組。例如,與 PostgreSQL 一起使用 org.flywaydb:flyway-database-postgresql
,與 MySQL 一起使用 org.flywaydb:flyway-mysql
。有關更多詳細資訊,請參閱Flyway 文件。
通常,遷移是形式為 V<VERSION>__<NAME>.sql
的指令碼(其中 <VERSION>
是以下劃線分隔的版本,例如“1”或“2_1”)。預設情況下,它們位於名為 classpath:db/migration
的目錄中,但你可以透過設定 spring.flyway.locations
來修改該位置。這是一個由一個或多個 classpath:
或 filesystem:
位置組成的逗號分隔列表。例如,以下配置將在預設的 classpath 位置和 /opt/migration
目錄中搜索指令碼
-
屬性
-
YAML
spring.flyway.locations=classpath:db/migration,filesystem:/opt/migration
spring:
flyway:
locations: "classpath:db/migration,filesystem:/opt/migration"
你還可以新增一個特殊的 {vendor}
佔位符來使用特定於供應商的指令碼。假設以下配置
-
屬性
-
YAML
spring.flyway.locations=classpath:db/migration/{vendor}
spring:
flyway:
locations: "classpath:db/migration/{vendor}"
前面的配置沒有使用 db/migration
,而是根據資料庫的型別設定要使用的目錄(例如,MySQL 使用 db/migration/mysql
)。支援的資料庫列表可在 DatabaseDriver
中找到。
遷移也可以用 Java 編寫。Flyway 將自動配置實現 JavaMigration
的任何 bean。
FlywayProperties
提供了 Flyway 的大部分設定以及一小部分額外屬性,可用於停用遷移或關閉位置檢查。如果你需要對配置進行更多控制,請考慮註冊一個實現 FlywayConfigurationCustomizer
的 bean。
Spring Boot 呼叫 Flyway.migrate()
執行資料庫遷移。如果你想進行更多控制,請提供一個實現 @Bean
並實現 FlywayMigrationStrategy
的 bean。
Flyway 支援 SQL 和 Java 回撥。要使用基於 SQL 的回撥,請將回調指令碼放在 classpath:db/migration
目錄中。要使用基於 Java 的回撥,請建立一個或多個實現 Callback
的 bean。任何此類 bean 都會自動註冊到 Flyway
中。它們可以使用 @Order
或實現 Ordered
來進行排序。
預設情況下,Flyway 會自動注入上下文中的 (@Primary
) DataSource
並將其用於遷移。如果你想使用不同的 DataSource
,可以建立一個並將其 @Bean
標記為 @FlywayDataSource
。如果你這樣做並且想要兩個資料來源(例如保留主要的自動配置 DataSource
),請記住將 @Bean
註解的 defaultCandidate
屬性設定為 false
。另外,你也可以透過在外部屬性中設定 spring.flyway.[url,user,password]
來使用 Flyway 原生的 DataSource
。設定 spring.flyway.url
或 spring.flyway.user
中的任何一個都足以使 Flyway 使用自己的 DataSource
。如果這三個屬性中的任何一個未設定,則將使用其對應的 spring.datasource
屬性的值。
你還可以使用 Flyway 為特定場景提供資料。例如,你可以將測試專用的遷移放在 src/test/resources
中,並且它們只在你的應用啟動進行測試時執行。此外,你可以使用特定於 profile 的配置來定製 spring.flyway.locations
,以便某些遷移僅在特定 profile 啟用時執行。例如,在 application-dev.properties
中,你可以指定以下設定
-
屬性
-
YAML
spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration
spring:
flyway:
locations: "classpath:/db/migration,classpath:/dev/db/migration"
透過這種設定,dev/db/migration
中的遷移僅在 dev
profile 啟用時執行。
在啟動時執行 Liquibase 資料庫遷移
要在啟動時自動執行 Liquibase 資料庫遷移,請將 org.liquibase:liquibase-core
新增到你的 classpath 中。
將 |
預設情況下,主 change log 從 db/changelog/db.changelog-master.yaml
讀取,但你可以透過設定 spring.liquibase.change-log
來更改位置。除了 YAML,Liquibase 還支援 JSON、XML 和 SQL change log 格式。
預設情況下,Liquibase 會自動注入上下文中的 (@Primary
) DataSource
並將其用於遷移。如果你需要使用不同的 DataSource
,可以建立一個並將其 @Bean
標記為 @LiquibaseDataSource
。如果你這樣做並且想要兩個資料來源(例如保留主要的自動配置 DataSource
),請記住將 @Bean
註解的 defaultCandidate
屬性設定為 false
。另外,你也可以透過在外部屬性中設定 spring.liquibase.[driver-class-name,url,user,password]
來使用 Liquibase 原生的 DataSource
。設定 spring.liquibase.url
或 spring.liquibase.user
中的任何一個都足以使 Liquibase 使用自己的 DataSource
。如果這三個屬性中的任何一個未設定,則將使用其對應的 spring.datasource
屬性的值。
有關可用設定的詳細資訊,例如上下文、預設 schema 等,請參閱 LiquibaseProperties
。
如果你想在使用 Liquibase
例項之前對其進行定製,你也可以使用 Customizer<Liquibase>
bean。
將 Flyway 用於僅測試遷移
如果你想建立用於填充測試資料庫的 Flyway 遷移,請將它們放在 src/test/resources/db/migration
中。例如,名為 src/test/resources/db/migration/V9999__test-data.sql
的檔案將在生產遷移之後執行,並且僅在你執行測試時執行。你可以使用此檔案來建立所需的測試資料。此檔案不會打包到你的 uber jar 或容器中。
將 Liquibase 用於僅測試遷移
如果你想建立用於填充測試資料庫的 Liquibase 遷移,你必須建立一個包含生產 changelog 的測試 changelog。
首先,你需要配置 Liquibase 在執行測試時使用不同的 changelog。一種方法是建立一個 Spring Boot 的 test
profile,並將 Liquibase 屬性放在其中。為此,建立一個名為 src/test/resources/application-test.properties
的檔案,並在其中放入以下屬性
-
屬性
-
YAML
spring.liquibase.change-log=classpath:/db/changelog/db.changelog-test.yaml
spring:
liquibase:
change-log: "classpath:/db/changelog/db.changelog-test.yaml"
這將配置 Liquibase 在 test
profile 中執行時使用不同的 changelog。
現在在 src/test/resources/db/changelog/db.changelog-test.yaml
建立 changelog 檔案
databaseChangeLog:
- include:
file: classpath:/db/changelog/db.changelog-master.yaml
- changeSet:
runOrder: "last"
id: "test"
changes:
# Insert your changes here
此 changelog 將在測試執行時使用,並且不會打包到你的 uber jar 或容器中。它包含生產 changelog,然後宣告一個新的 changeset,其 runOrder: last
設定指定它在所有生產 changesets 執行後執行。你現在可以使用例如 insert changeset 插入資料,或者使用 sql changeset 直接執行 SQL。
最後要做的是配置 Spring Boot 在執行測試時啟用 test
profile。為此,你可以將 @ActiveProfiles("test")
註解新增到你的帶有 @SpringBootTest
註解的測試類中。
依賴於已初始化的資料庫
資料庫初始化在應用啟動時作為應用上下文重新整理的一部分執行。為了允許在啟動期間訪問已初始化的資料庫,充當資料庫初始化器和需要資料庫已初始化的 bean 會被自動檢測到。其初始化依賴於資料庫已初始化的 bean 會被配置為依賴於初始化它的 bean。如果在啟動期間,你的應用嘗試訪問資料庫而資料庫尚未初始化,你可以配置額外檢測初始化資料庫並需要資料庫已初始化的 bean。
檢測資料庫初始化器
Spring Boot 將自動檢測初始化 SQL 資料庫的以下型別的 bean
如果你正在使用第三方資料庫初始化庫的 starter,它可能會提供一個檢測器,以便自動檢測其他型別的 bean。要讓其他 bean 被檢測到,請在 META-INF/spring.factories
中註冊一個 DatabaseInitializerDetector
的實現。
檢測依賴於資料庫初始化的 bean
Spring Boot 將自動檢測依賴於資料庫初始化的以下型別的 bean
-
AbstractEntityManagerFactoryBean
(除非spring.jpa.defer-datasource-initialization
設定為true
) -
DSLContext
(jOOQ) -
EntityManagerFactory
(除非spring.jpa.defer-datasource-initialization
設定為true
)
如果你正在使用第三方資料訪問庫的 starter,它可能會提供一個檢測器,以便自動檢測其他型別的 bean。要讓其他 bean 被檢測到,請在 META-INF/spring.factories
中註冊一個 DependsOnDatabaseInitializationDetector
的實現。另外,也可以使用 @DependsOnDatabaseInitialization
註解 bean 的類或其 @Bean
方法。