初始化 DataSource

org.springframework.jdbc.datasource.init 包提供了初始化現有 DataSource 的支援。嵌入式資料庫支援為應用程式建立和初始化 DataSource 提供了一種選項。然而,有時您可能需要初始化執行在某個伺服器上的例項。

使用 Spring XML 初始化資料庫

如果您想初始化資料庫並能夠提供對 DataSource bean 的引用,可以使用 spring-jdbc 名稱空間中的 initialize-database 標籤

<jdbc:initialize-database data-source="dataSource">
	<jdbc:script location="classpath:com/foo/sql/db-schema.sql"/>
	<jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/>
</jdbc:initialize-database>

前面的示例對資料庫運行了兩個指定的指令碼。第一個指令碼建立 Schema,第二個用測試資料集填充表。指令碼位置也可以是帶有萬用字元的模式,使用 Spring 資源中常用的 Ant 風格(例如,classpath*:/com/foo/**/sql/*-data.sql)。如果您使用模式,指令碼將按照其 URL 或檔名的字典順序執行。

資料庫初始化器的預設行為是無條件執行提供的指令碼。這可能並非總是您想要的——例如,如果您對已經包含測試資料的資料庫執行指令碼。透過遵循先建立表然後插入資料的常見模式(如前所示),可以降低意外刪除資料的可能性。如果表已經存在,第一步會失敗。

然而,為了更好地控制現有資料的建立和刪除,XML 名稱空間提供了一些附加選項。第一個是控制初始化開啟或關閉的標誌。您可以根據環境設定此標誌(例如,從系統屬性或環境 Bean 中獲取布林值)。以下示例從系統屬性獲取值

<jdbc:initialize-database data-source="dataSource"
	enabled="#{systemProperties.INITIALIZE_DATABASE}"> (1)
	<jdbc:script location="..."/>
</jdbc:initialize-database>
1 從名為 INITIALIZE_DATABASE 的系統屬性中獲取 enabled 的值。

第二個控制現有資料處理方式的選項是更加容忍失敗。為此,您可以控制初始化器忽略它從指令碼執行的 SQL 中某些錯誤的能力,如下例所示

<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
	<jdbc:script location="..."/>
</jdbc:initialize-database>

在前面的示例中,我們表示期望指令碼有時會對空資料庫執行,並且指令碼中包含一些因此會失敗的 DROP 語句。因此,失敗的 SQL DROP 語句將被忽略,但其他失敗將導致異常。如果您的 SQL 方言不支援 DROP …​ IF EXISTS(或類似語法),但您想在重新建立之前無條件地刪除所有測試資料,則此功能非常有用。在這種情況下,第一個指令碼通常是一組 DROP 語句,後跟一組 CREATE 語句。

ignore-failures 選項可以設定為 NONE(預設)、DROPS(忽略失敗的 DROP 語句)或 ALL(忽略所有失敗)。

每個語句應由 `;` 分隔,或者如果指令碼中完全沒有 `;` 字元,則使用新行。您可以全域性控制,也可以按指令碼控制,如下例所示

<jdbc:initialize-database data-source="dataSource" separator="@@"> (1)
	<jdbc:script location="classpath:com/myapp/sql/db-schema.sql" separator=";"/> (2)
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-1.sql"/>
	<jdbc:script location="classpath:com/myapp/sql/db-test-data-2.sql"/>
</jdbc:initialize-database>
1 將分隔符指令碼設定為 @@
2 db-schema.sql 的分隔符設定為 `;`。

在此示例中,兩個 test-data 指令碼使用 @@ 作為語句分隔符,只有 db-schema.sql 使用 `;`。此配置指定預設分隔符為 @@,併為 db-schema 指令碼覆蓋了此預設設定。

如果您需要比 XML 名稱空間提供的更多控制,可以直接使用 DataSourceInitializer 並將其定義為應用程式中的一個元件。

依賴於資料庫的其他元件的初始化

大多數應用程式(那些在 Spring 上下文啟動後才使用資料庫的應用程式)可以毫無額外複雜性地使用資料庫初始化器。如果您的應用程式不是此類,您可能需要閱讀本節的其餘部分。

資料庫初始化器依賴於 DataSource 例項,並在其初始化回撥中(類似於 XML bean 定義中的 init-method、元件中的 `@PostConstruct` 方法或實現 InitializingBean 的元件中的 afterPropertiesSet() 方法)執行提供的指令碼。如果其他 Bean 依賴於同一個資料來源並在初始化回撥中使用該資料來源,可能會出現問題,因為資料尚未初始化。一個常見的例子是急切初始化並在應用啟動時從資料庫載入資料的快取。

為了解決這個問題,您有兩個選項:將您的快取初始化策略更改為更晚的階段,或確保資料庫初始化器首先被初始化。

如果應用程式在您的控制範圍內,更改快取初始化策略可能會很容易。實施此策略的一些建議包括

  • 讓快取在使用時延遲初始化,這樣可以提高應用程式啟動時間。

  • 讓您的快取或初始化快取的獨立元件實現 LifecycleSmartLifecycle。當應用程式上下文啟動時,您可以透過設定 SmartLifecycleautoStartup 標誌自動啟動它,也可以透過在封閉上下文上呼叫 ConfigurableApplicationContext.start() 手動啟動 Lifecycle

  • 使用 Spring ApplicationEvent 或類似的自定義觀察者機制來觸發快取初始化。ContextRefreshedEvent 總是在上下文準備就緒(所有 Bean 都初始化後)時釋出,所以這通常是一個有用的鉤子(這是 SmartLifecycle 的預設工作方式)。

確保資料庫初始化器首先被初始化也可能很容易。實施此策略的一些建議包括

  • 依靠 Spring BeanFactory 的預設行為,即 Bean 按照註冊順序初始化。您可以透過在 XML 配置中採用一組 `` 元素來安排應用程式模組的順序,並確保資料庫和資料庫初始化首先列出,從而輕鬆實現這一點。

  • 分離 DataSource 和使用它的業務元件,並透過將它們放在不同的 ApplicationContext 例項中來控制它們的啟動順序(例如,父上下文包含 DataSource,子上下文包含業務元件)。這種結構在 Spring Web 應用中很常見,但也適用於更廣泛的場景。