資料庫初始化

SQL 資料庫可以用不同的方式初始化,取決於您的堆疊。當然,如果資料庫是獨立的程序,您也可以手動執行。建議使用單一機制來產生結構描述。

使用 Hibernate 初始化資料庫

您可以設定 spring.jpa.hibernate.ddl-auto 來控制 Hibernate 的資料庫初始化。支援的值為 nonevalidateupdatecreatecreate-drop。Spring Boot 會根據您是否正在使用嵌入式資料庫來選擇預設值。嵌入式資料庫是透過查看 Connection 類型和 JDBC URL 來識別。hsqldbh2derby 是嵌入式資料庫,其他則不是。如果識別出嵌入式資料庫且未偵測到結構描述管理器 (Flyway 或 Liquibase),則 ddl-auto 預設為 create-drop。在所有其他情況下,預設為 none

當從記憶體資料庫切換到「真實」資料庫時,請小心不要對新平台中表格和資料的存在做出假設。您必須明確設定 ddl-auto 或使用其他機制之一來初始化資料庫。

您可以透過啟用 org.hibernate.SQL logger 來輸出結構描述建立。如果您啟用除錯模式,這會自動為您完成。

此外,如果 Hibernate 從頭開始建立結構描述(也就是說,如果 ddl-auto 屬性設定為 createcreate-drop),則在啟動時會執行類別路徑根目錄中名為 import.sql 的檔案。如果您很小心,這對於示範和測試可能很有用,但在生產環境中,您可能不希望它存在於類別路徑中。這是 Hibernate 的功能(與 Spring 無關)。

使用基本 SQL 腳本初始化資料庫

Spring Boot 可以自動建立 JDBC DataSource 或 R2DBC ConnectionFactory 的結構描述(DDL 腳本)並初始化其資料(DML 腳本)。

預設情況下,它會從 optional:classpath*:schema.sql 載入結構描述腳本,並從 optional:classpath*:data.sql 載入資料腳本。這些結構描述和資料腳本的位置可以使用 spring.sql.init.schema-locationsspring.sql.init.data-locations 分別自訂。optional: 前綴表示即使檔案不存在,應用程式也會啟動。若要在檔案不存在時讓應用程式啟動失敗,請移除 optional: 前綴。

此外,Spring Boot 會處理 optional:classpath*:schema-${platform}.sqloptional:classpath*:data-${platform}.sql 檔案(如果存在),其中 ${platform}spring.sql.init.platform 的值。這可讓您在必要時切換到資料庫特定的腳本。例如,您可以選擇將其設定為資料庫的供應商名稱(hsqldbh2oraclemysqlpostgresql 等)。

預設情況下,SQL 資料庫初始化僅在使用嵌入式記憶體資料庫時執行。若要始終初始化 SQL 資料庫,無論其類型為何,請將 spring.sql.init.mode 設定為 always。同樣地,若要停用初始化,請將 spring.sql.init.mode 設定為 never。預設情況下,Spring Boot 會啟用其基於腳本的資料庫初始化程式的快速失敗功能。這表示如果腳本導致例外,則應用程式啟動會失敗。您可以透過設定 spring.sql.init.continue-on-error 來調整該行為。

基於腳本的 DataSource 初始化預設會在建立任何 JPA EntityManagerFactory bean 之前執行。schema.sql 可以用於建立 JPA 管理實體的結構描述,而 data.sql 可以用於填充它。雖然我們不建議使用多種資料來源初始化技術,但如果您希望基於腳本的 DataSource 初始化能夠建立在 Hibernate 執行的結構描述建立之上,請將 spring.jpa.defer-datasource-initialization 設定為 true。這會將資料來源初始化延遲到建立和初始化任何 EntityManagerFactory bean 之後。然後,schema.sql 可以用於對 Hibernate 執行的任何結構描述建立進行新增,而 data.sql 可以用於填充它。

初始化腳本支援 -- 用於單行註解,以及 /* */ 用於區塊註解。不支援其他註解格式。

如果您正在使用更高等級的資料庫遷移工具,例如 Flyway 或 Liquibase,您應該單獨使用它們來建立和初始化結構描述。不建議將基本的 schema.sqldata.sql 腳本與 Flyway 或 Liquibase 一起使用,並且在未來的版本中將會移除支援。

如果您需要使用更高等級的資料庫遷移工具來初始化測試資料,請參閱關於 FlywayLiquibase 的章節。

初始化 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 來明確關閉初始化。

使用更高等級的資料庫遷移工具

Spring Boot 支援兩個更高等級的遷移工具:FlywayLiquibase

在啟動時執行 Flyway 資料庫遷移

若要在啟動時自動執行 Flyway 資料庫遷移,請將適當的 Flyway 模組新增至您的類別路徑。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: 位置的清單。例如,以下組態將在預設類別路徑位置和 /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() 來執行資料庫遷移。如果您想要更多控制權,請提供實作 FlywayMigrationStrategy@Bean

Flyway 支援 SQL 和 Java 回呼。若要使用基於 SQL 的回呼,請將回呼腳本放在 classpath:db/migration 目錄中。若要使用基於 Java 的回呼,請建立一個或多個實作 Callback 的 bean。任何此類 bean 都會自動向 Flyway 註冊。它們可以使用 @Order 或實作 Ordered 來排序。實作已棄用的 FlywayCallback 介面的 Bean 也可以被偵測到,但是它們不能與 Callback bean 一起使用。

預設情況下,Flyway 會自動裝配您內容中的 (@Primary) DataSource,並將其用於遷移。如果您想使用不同的 DataSource,您可以建立一個並將其 @Bean 標記為 @FlywayDataSource。如果您這樣做並且想要兩個資料來源,請記住建立另一個資料來源並將其標記為 @Primary。或者,您可以使用 Flyway 的原生 DataSource,方法是在外部屬性中設定 spring.flyway.[url,user,password]。設定 spring.flyway.urlspring.flyway.user 中的任何一個都足以讓 Flyway 使用其自己的 DataSource。如果這三個屬性中的任何一個尚未設定,則將使用其等效的 spring.datasource 屬性的值。

您也可以使用 Flyway 為特定情境提供資料。例如,您可以將測試特定的遷移放在 src/test/resources 中,並且它們僅在您的應用程式啟動以進行測試時執行。此外,您可以使用設定檔特定的組態來自訂 spring.flyway.locations,以便某些遷移僅在特定設定檔處於作用中狀態時執行。例如,在 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 設定檔處於作用中狀態時執行。

在啟動時執行 Liquibase 資料庫遷移

若要在啟動時自動執行 Liquibase 資料庫遷移,請將 org.liquibase:liquibase-core 新增至您的類別路徑。

當您將 org.liquibase:liquibase-core 新增至您的類別路徑時,資料庫遷移預設會在應用程式啟動期間和測試執行之前執行。可以使用 spring.liquibase.enabled 屬性來自訂此行為,在 maintest 組態中設定不同的值。無法使用兩種不同的方式來初始化資料庫(例如,應用程式啟動使用 Liquibase,測試執行使用 JPA)。

預設情況下,主要變更記錄檔是從 db/changelog/db.changelog-master.yaml 讀取,但您可以透過設定 spring.liquibase.change-log 來變更位置。除了 YAML 之外,Liquibase 也支援 JSON、XML 和 SQL 變更記錄檔格式。

預設情況下,Liquibase 會自動裝配您內容中的 (@Primary) DataSource,並將其用於遷移。如果您需要使用不同的 DataSource,您可以建立一個並將其 @Bean 標記為 @LiquibaseDataSource。如果您這樣做並且想要兩個資料來源,請記住建立另一個資料來源並將其標記為 @Primary。或者,您可以使用 Liquibase 的原生 DataSource,方法是在外部屬性中設定 spring.liquibase.[driver-class-name,url,user,password]。設定 spring.liquibase.urlspring.liquibase.user 中的任何一個都足以讓 Liquibase 使用其自己的 DataSource。如果這三個屬性中的任何一個尚未設定,則將使用其等效的 spring.datasource 屬性的值。

請參閱 LiquibaseProperties 以取得有關可用設定的詳細資訊,例如內容、預設結構描述和其他設定。

將 Flyway 用於僅限測試的遷移

如果您想要建立 Flyway 遷移來填充您的測試資料庫,請將它們放在 src/test/resources/db/migration 中。例如,名為 src/test/resources/db/migration/V9999__test-data.sql 的檔案將在您的生產遷移之後執行,並且僅在您執行測試時執行。您可以使用此檔案來建立所需的測試資料。此檔案不會封裝在您的 uber jar 或容器中。

將 Liquibase 用於僅限測試的遷移

如果您想要建立 Liquibase 遷移來填充您的測試資料庫,您必須建立一個也包含生產變更記錄檔的測試變更記錄檔。

首先,您需要組態 Liquibase 在執行測試時使用不同的變更記錄檔。一種方法是建立 Spring Boot test 設定檔,並將 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 設定檔中執行時使用不同的變更記錄檔。

現在在 src/test/resources/db/changelog/db.changelog-test.yaml 建立變更記錄檔

databaseChangeLog:
  - include:
      file: classpath:/db/changelog/db.changelog-master.yaml
  - changeSet:
      runOrder: "last"
      id: "test"
      changes:
        # Insert your changes here

此變更記錄檔將在執行測試時使用,並且不會封裝在您的 uber jar 或容器中。它包含生產變更記錄檔,然後宣告一個新的變更集,其 runOrder: last 設定指定它在所有生產變更集執行後執行。您現在可以使用例如 insert 變更集 來插入資料,或使用 sql 變更集 來直接執行 SQL。

最後要做的是組態 Spring Boot 在執行測試時啟動 test 設定檔。若要執行此操作,您可以將 @ActiveProfiles("test") 註解新增至您的 @SpringBootTest 註解測試類別。

依賴已初始化的資料庫

資料庫初始化是在應用程式啟動時作為應用程式內容重新整理的一部分執行的。為了允許在啟動期間存取已初始化的資料庫,會自動偵測充當資料庫初始化程式的 bean 和需要已初始化資料庫的 bean。其初始化依賴於資料庫已初始化的 bean 被組態為依賴於那些初始化它的 bean。如果在啟動期間,您的應用程式嘗試存取資料庫且尚未初始化,您可以組態對初始化資料庫和需要已初始化資料庫的 bean 進行額外偵測。

偵測資料庫初始化程式

Spring Boot 將自動偵測以下類型的 bean,這些 bean 初始化 SQL 資料庫

  • DataSourceScriptDatabaseInitializer

  • EntityManagerFactory

  • Flyway

  • FlywayMigrationInitializer

  • R2dbcScriptDatabaseInitializer

  • SpringLiquibase

如果您正在使用第三方啟動器來進行資料庫初始化程式庫,它可能會提供偵測器,以便也自動偵測其他類型的 bean。若要讓其他 bean 被偵測到,請在 META-INF/spring.factories 中註冊 DatabaseInitializerDetector 的實作。

偵測依賴資料庫初始化的 Bean

Spring Boot 將自動偵測以下類型的 bean,這些 bean 依賴於資料庫初始化

  • AbstractEntityManagerFactoryBean(除非 spring.jpa.defer-datasource-initialization 設定為 true

  • DSLContext (jOOQ)

  • EntityManagerFactory(除非 spring.jpa.defer-datasource-initialization 設定為 true

  • JdbcClient

  • JdbcOperations

  • NamedParameterJdbcOperations

如果您正在使用第三方啟動器資料存取程式庫,它可能會提供偵測器,以便也自動偵測其他類型的 bean。若要讓其他 bean 被偵測到,請在 META-INF/spring.factories 中註冊 DependsOnDatabaseInitializationDetector 的實作。或者,使用 @DependsOnDatabaseInitialization 註解 bean 的類別或其 @Bean 方法。