嵌入式数据库支持
为什么使用嵌入式数据库?
嵌入式数据库在项目开发阶段非常有用,因为它具有轻量级的特性。其优点包括配置简便、启动速度快、易于测试,以及在开发过程中能够快速调整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数据库,该数据库的内容由类路径(classpath)根目录下的schema.sql和test-data.sql文件中的SQL语句生成。此外,作为最佳实践,这个嵌入式数据库会被赋予一个唯一生成的名称。该嵌入式数据库以javax.sql.DataSource类型的bean形式提供给Spring容器,从而可以根据需要将其注入到数据访问对象中。
有关所有支持选项的更多详细信息,请参阅EmbeddedDatabaseBuilder的javadoc。
选择嵌入式数据库类型
本节介绍了如何选择Spring所支持的三种嵌入式数据库中的一种。它包括以下主题:
使用HSQL
Spring 支持 HSQL 1.8.0 及以上版本。如果没有明确指定数据库类型,HSQL 将作为默认的嵌入式数据库使用。要明确指定使用 HSQL,需要将 embedded-database 标签的 type 属性设置为 HSQL。如果使用构建器 API,则调用 setType(EmbeddedDatabaseType) 方法,并传入 EmbeddedDatabaseType.HSQL 作为参数。
使用H2
Spring支持H2数据库。要启用H2,将embedded-database标签的type属性设置为H2。如果您使用构建器API,请调用setType(EmbeddedDatabaseType)方法,并传入EmbeddedDatabaseType.H2作为参数。
使用 Derby
Spring支持Apache Derby 10.5及更高版本。要启用Derby,请将embedded-database标签的type属性设置为DERBY。如果您使用构建器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框架,并按照创建嵌入式数据库中的描述,在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定义配置文件的激活上有所差异。
这种错误的根本原因在于,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 社区贡献扩展程序。