嵌入式資料庫支援

org.springframework.jdbc.datasource.embedded 包提供了對嵌入式 Java 資料庫引擎的支援。原生支援 HSQLH2Derby。您還可以使用可擴充套件的 API 插入新的嵌入式資料庫型別和 DataSource 實現。

為何使用嵌入式資料庫?

嵌入式資料庫因其輕量級特性在專案開發階段非常有用。其優勢包括易於配置、啟動快速、可測試性強,以及在開發過程中快速迭代 SQL 的能力。

建立嵌入式資料庫

您可以將嵌入式資料庫例項作為 bean 暴露,如下例所示

  • Java

  • Kotlin

  • Xml

@Bean
DataSource dataSource() {
	return new EmbeddedDatabaseBuilder()
			.generateUniqueName(true)
			.setType(EmbeddedDatabaseType.H2)
			.addScripts("schema.sql", "test-data.sql")
			.build();
}
@Bean
fun dataSource() = EmbeddedDatabaseBuilder()
	.generateUniqueName(true)
	.setType(EmbeddedDatabaseType.H2)
	.addScripts("schema.sql", "test-data.sql")
	.build()
<jdbc:embedded-database id="dataSource" generate-name="true" type="H2">
	<jdbc:script location="classpath:schema.sql"/>
	<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

上述配置建立了一個嵌入式 H2 資料庫,該資料庫從類路徑根目錄下的 schema.sqltest-data.sql 資源載入 SQL 進行填充。此外,作為最佳實踐,嵌入式資料庫被分配了一個唯一生成的名稱。該嵌入式資料庫作為型別為 javax.sql.DataSource 的 bean 提供給 Spring 容器,然後可以根據需要注入到資料訪問物件中。

有關所有支援選項的更多詳細資訊,請參閱 EmbeddedDatabaseBuilder 的 javadoc

選擇嵌入式資料庫型別

本節介紹瞭如何選擇 Spring 支援的三種嵌入式資料庫之一。包括以下主題

使用 HSQL

Spring 支援 HSQL 1.8.0 及以上版本。如果未明確指定型別,HSQL 是預設的嵌入式資料庫。要顯式指定 HSQL,請將 embedded-database 標籤的 type 屬性設定為 HSQL。如果使用 builder API,請呼叫 setType(EmbeddedDatabaseType) 方法並傳入 EmbeddedDatabaseType.HSQL

使用 H2

Spring 支援 H2 資料庫。要啟用 H2,請將 embedded-database 標籤的 type 屬性設定為 H2。如果使用 builder API,請呼叫 setType(EmbeddedDatabaseType) 方法並傳入 EmbeddedDatabaseType.H2

使用 Derby

Spring 支援 Apache Derby 10.5 及以上版本。要啟用 Derby,請將 embedded-database 標籤的 type 屬性設定為 DERBY。如果使用 builder API,請呼叫 setType(EmbeddedDatabaseType) 方法並傳入 EmbeddedDatabaseType.DERBY

自定義嵌入式資料庫型別

雖然每種支援的型別都帶有預設連線設定,但如有必要可以自定義它們。以下示例使用帶有自定義驅動程式的 H2

  • Java

  • Kotlin

@Configuration
public class DataSourceConfig {

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
				.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
						.customizeConfigurer(H2, this::customize))
				.addScript("schema.sql")
				.build();
	}

	private EmbeddedDatabaseConfigurer customize(EmbeddedDatabaseConfigurer defaultConfigurer) {
		return new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
			@Override
			public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
				super.configureConnectionProperties(properties, databaseName);
				properties.setDriverClass(CustomDriver.class);
			}
		};
	}
}
@Configuration
class DataSourceConfig {

	@Bean
	fun dataSource(): DataSource {
		return EmbeddedDatabaseBuilder()
			.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
				.customizeConfigurer(EmbeddedDatabaseType.H2) { this.customize(it) })
			.addScript("schema.sql")
			.build()
	}

	private fun customize(defaultConfigurer: EmbeddedDatabaseConfigurer): EmbeddedDatabaseConfigurer {
		return object : EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
			override fun configureConnectionProperties(
				properties: ConnectionProperties,
				databaseName: String
			) {
				super.configureConnectionProperties(properties, databaseName)
				properties.setDriverClass(CustomDriver::class.java)
			}
		}
	}
}

使用嵌入式資料庫測試資料訪問邏輯

嵌入式資料庫提供了一種輕量級的方式來測試資料訪問程式碼。下一個示例是使用嵌入式資料庫的資料訪問整合測試模板。當嵌入式資料庫不需要在測試類之間重用時,使用此類模板對於一次性測試非常有用。但是,如果您希望建立在測試套件內共享的嵌入式資料庫,請考慮使用 Spring TestContext Framework 並按照 建立嵌入式資料庫 中所述,將嵌入式資料庫配置為 Spring ApplicationContext 中的 bean。以下列表顯示了測試模板

  • Java

  • Kotlin

public class DataAccessIntegrationTestTemplate {

	private EmbeddedDatabase db;

	@BeforeEach
	public void setUp() {
		// creates an HSQL in-memory database populated from default scripts
		// classpath:schema.sql and classpath:data.sql
		db = new EmbeddedDatabaseBuilder()
				.generateUniqueName(true)
				.addDefaultScripts()
				.build();
	}

	@Test
	public void testDataAccess() {
		JdbcTemplate template = new JdbcTemplate(db);
		template.query( /* ... */ );
	}

	@AfterEach
	public void tearDown() {
		db.shutdown();
	}

}
class DataAccessIntegrationTestTemplate {

	private lateinit var db: EmbeddedDatabase

	@BeforeEach
	fun setUp() {
		// creates an HSQL in-memory database populated from default scripts
		// classpath:schema.sql and classpath:data.sql
		db = EmbeddedDatabaseBuilder()
				.generateUniqueName(true)
				.addDefaultScripts()
				.build()
	}

	@Test
	fun testDataAccess() {
		val template = JdbcTemplate(db)
		template.query( /* ... */)
	}

	@AfterEach
	fun tearDown() {
		db.shutdown()
	}
}

為嵌入式資料庫生成唯一名稱

如果測試套件無意中嘗試重新建立同一個資料庫的額外例項,開發團隊經常會遇到嵌入式資料庫錯誤。如果 XML 配置檔案或 @Configuration 類負責建立嵌入式資料庫,並且相應的配置在同一個測試套件(即同一個 JVM 程序)中的多個測試場景中被重用時,這很容易發生——例如,針對嵌入式資料庫的整合測試,這些資料庫的 ApplicationContext 配置僅在啟用的 bean 定義 profile 方面有所不同。

此類錯誤的根本原因在於 Spring 的 EmbeddedDatabaseFactory(在 <jdbc:embedded-database> XML 名稱空間元素和用於 Java 配置的 EmbeddedDatabaseBuilder 內部使用)在未另行指定的情況下,會將嵌入式資料庫的名稱設定為 testdb。對於 <jdbc:embedded-database> 的情況,嵌入式資料庫通常被分配一個等於 bean 的 id 的名稱(通常是像 dataSource 這樣的名稱)。因此,後續嘗試建立嵌入式資料庫不會產生新的資料庫。相反,而是重用了相同的 JDBC 連線 URL,並且嘗試建立新嵌入式資料庫實際上指向了從相同配置建立的現有嵌入式資料庫。

為了解決這個常見問題,Spring Framework 4.2 提供了為嵌入式資料庫生成唯一名稱的支援。要啟用生成名稱的使用,請使用以下選項之一。

  • EmbeddedDatabaseFactory.setGenerateUniqueDatabaseName()

  • EmbeddedDatabaseBuilder.generateUniqueName()

  • <jdbc:embedded-database generate-name="true" …​ >

擴充套件嵌入式資料庫支援

您可以透過兩種方式擴充套件 Spring JDBC 嵌入式資料庫支援

  • 實現 EmbeddedDatabaseConfigurer 以支援新的嵌入式資料庫型別。

  • 實現 DataSourceFactory 以支援新的 DataSource 實現,例如用於管理嵌入式資料庫連線的連線池。

我們鼓勵您透過 GitHub Issues 向 Spring 社群貢獻擴充套件。