对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

嵌入式数据库支持

org.springframework.jdbc.datasource.embedded 包提供了对嵌入式 Java 数据库引擎的支持。原生支持 HSQLH2Derby。 您还可以使用可扩展的 API 来接入新的嵌入式数据库类型和 DataSource 实现。spring-doc.cadn.net.cn

为什么要使用嵌入式数据库?

由于嵌入式数据库具有轻量级的特性,因此在项目开发阶段非常有用。其优势包括配置简单、启动迅速、易于测试,以及能够在开发过程中快速迭代 SQL。spring-doc.cadn.net.cn

创建嵌入式数据库

你可以将嵌入式数据库实例作为 Bean 暴露出来,如下例所示:spring-doc.cadn.net.cn

@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 容器,随后可根据需要注入到数据访问对象中。spring-doc.cadn.net.cn

有关所有支持选项的更多详情,请参阅 EmbeddedDatabaseBuilder 的 Javadocspring-doc.cadn.net.cn

选择嵌入式数据库类型

本节介绍如何选择 Spring 支持的三种嵌入式数据库之一。内容包括以下主题:spring-doc.cadn.net.cn

使用 HSQL

Spring 支持 HSQL 1.8.0 及以上版本。如果没有显式指定数据库类型,HSQL 将作为默认的嵌入式数据库。要显式指定 HSQL,请将 type 标签的 embedded-database 属性设置为 HSQL。如果使用构建器 API,则调用 setType(EmbeddedDatabaseType) 方法,并传入 EmbeddedDatabaseType.HSQLspring-doc.cadn.net.cn

使用 H2

Spring 支持 H2 数据库。要启用 H2,请将 type 标签的 embedded-database 属性设置为 H2。如果您使用构建器 API,请调用 setType(EmbeddedDatabaseType) 方法,并传入 EmbeddedDatabaseType.H2spring-doc.cadn.net.cn

使用 Derby

Spring 支持 Apache Derby 10.5 及以上版本。要启用 Derby,请将 type 标签的 embedded-database 属性设置为 DERBY。如果您使用构建器 API,则调用 setType(EmbeddedDatabaseType) 方法并传入 EmbeddedDatabaseType.DERBYspring-doc.cadn.net.cn

自定义嵌入式数据库类型

尽管每种受支持的类型都带有默认的连接设置,但在必要时也可以对其进行自定义。以下示例使用 H2 数据库并指定自定义驱动程序:spring-doc.cadn.net.cn

@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 框架,并按照创建嵌入式数据库一节中所述,将嵌入式数据库配置为 Spring #jdbc-embedded-database 中的一个 Bean。 以下代码清单展示了该测试模板:spring-doc.cadn.net.cn

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 定义配置文件(profiles)方面有所不同。spring-doc.cadn.net.cn

此类错误的根本原因在于:Spring 的 EmbeddedDatabaseFactory<jdbc:embedded-database> XML 命名空间元素和用于 Java 配置的 EmbeddedDatabaseBuilder 内部均使用该类)在未另行指定时,会将嵌入式数据库的名称设为 testdb。对于 <jdbc:embedded-database> 的情况,嵌入式数据库通常会被赋予一个与 Bean 的 id 相同的名称(通常是类似 dataSource 的名称)。因此,后续创建嵌入式数据库的尝试并不会生成一个新的数据库,而是复用相同的 JDBC 连接 URL,导致创建新嵌入式数据库的操作实际上指向了由相同配置所创建的已有嵌入式数据库。spring-doc.cadn.net.cn

为了解决这一常见问题,Spring Framework 4.2 提供了为嵌入式数据库生成唯一名称的支持。要启用生成的名称,请使用以下选项之一。spring-doc.cadn.net.cn

扩展嵌入式数据库支持

您可以通过两种方式扩展 Spring JDBC 的嵌入式数据库支持:spring-doc.cadn.net.cn

  • 实现 EmbeddedDatabaseConfigurer 以支持一种新的嵌入式数据库类型。spring-doc.cadn.net.cn

  • 实现 DataSourceFactory 以支持新的 DataSource 实现,例如用于管理嵌入式数据库连接的连接池。spring-doc.cadn.net.cn

我们鼓励您在GitHub Issues上为Spring社区贡献扩展。spring-doc.cadn.net.cn