嵌入式数据库支持

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

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

在项目开发阶段使用嵌入式数据库是有益的,因为它具有轻量级的特性。优点包括易于配置、快速启动时间、可测试性以及在开发过程中快速演进SQL的能力。

使用Spring XML创建嵌入式数据库

如果您想在Spring ApplicationContext中将嵌入式数据库实例公开为一个bean,您可以在spring-jdbc命名空间中使用embedded-database标签:

<jdbc:embedded-database id="dataSource" generate-name="true">
	<jdbc:script location="classpath:schema.sql"/>
	<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

上述配置创建了一个嵌入式HSQL数据库,该数据库使用类路径中schema.sqltest-data.sql资源中的SQL进行填充。此外,作为最佳实践,嵌入式数据库被分配一个唯一生成的名称。嵌入式数据库作为javax.sql.DataSource类型的bean提供给Spring容器,然后可以根据需要将其注入到数据访问对象中。

通过编程方式创建嵌入式数据库

EmbeddedDatabaseBuilder类提供了一个流畅的API,用于通过编程方式构建嵌入式数据库。当您需要在独立环境或独立集成测试中创建嵌入式数据库时,可以使用这个功能,如下例所示:

  • Java

  • Kotlin

EmbeddedDatabase db = new EmbeddedDatabaseBuilder()
		.generateUniqueName(true)
		.setType(H2)
		.setScriptEncoding("UTF-8")
		.ignoreFailedDrops(true)
		.addScript("schema.sql")
		.addScripts("user_data.sql", "country_data.sql")
		.build();

// 对数据库执行操作(EmbeddedDatabase扩展了javax.sql.DataSource)

db.shutdown()
val db = EmbeddedDatabaseBuilder()
		.generateUniqueName(true)
		.setType(H2)
		.setScriptEncoding("UTF-8")
		.ignoreFailedDrops(true)
		.addScript("schema.sql")
		.addScripts("user_data.sql", "country_data.sql")
		.build()

// 对数据库执行操作(EmbeddedDatabase扩展了javax.sql.DataSource)

db.shutdown()

有关所有支持选项的详细信息,请参阅EmbeddedDatabaseBuilder的javadoc

您还可以使用EmbeddedDatabaseBuilder通过Java配置创建嵌入式数据库,如下例所示:

  • Java

  • Kotlin

@Configuration
public class DataSourceConfig {

	@Bean
	public DataSource dataSource() {
		return new EmbeddedDatabaseBuilder()
				.generateUniqueName(true)
				.setType(H2)
				.setScriptEncoding("UTF-8")
				.ignoreFailedDrops(true)
				.addScript("schema.sql")
				.addScripts("user_data.sql", "country_data.sql")
				.build();
	}
}
@Configuration
class DataSourceConfig {

	@Bean
	fun dataSource(): DataSource {
		return EmbeddedDatabaseBuilder()
				.generateUniqueName(true)
				.setType(H2)
				.setScriptEncoding("UTF-8")
				.ignoreFailedDrops(true)
				.addScript("schema.sql")
				.addScripts("user_data.sql", "country_data.sql")
				.build()
	}
}

选择嵌入式数据库类型

本节介绍了如何选择Spring支持的三种嵌入式数据库之一。包括以下主题:

使用HSQL

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

使用H2

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

使用Derby

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

使用嵌入式数据库测试数据访问逻辑

嵌入式数据库提供了一种轻量级的方式来测试数据访问代码。下面的示例是使用嵌入式数据库的数据访问集成测试模板。在嵌入式数据库不需要在测试类之间重复使用时,使用这样的模板可以很有用。但是,如果您希望创建一个在测试套件中共享的嵌入式数据库,请考虑使用Spring TestContext Framework并将嵌入式数据库配置为Spring ApplicationContext中的一个bean,如使用Spring XML创建嵌入式数据库通过编程方式创建嵌入式数据库中所述。以下代码显示了测试模板:

  • 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定义配置文件激活方面有所不同。

这类错误的根本原因在于Spring的EmbeddedDatabaseFactory(内部由<jdbc:embedded-database> XML命名空间元素和EmbeddedDatabaseBuilder用于Java配置所使用)如果没有另外指定,会将嵌入式数据库的名称设置为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社区贡献扩展。