15.8 内嵌数据库支持

org.springframework.jdbc.datasource.embedded包包含对内嵌Java数据库引擎的支持。如对HSQL, H2, and Derby原生支持,你还可以使用扩展API来嵌入新的数据库内嵌类型和Datasource实现。

15.8.1 为什么使用一个内嵌数据库?

内嵌数据库因为比较轻量级所以在开发阶段比较方便有用。包括配置比较容易,启动快,方便测试,并且在开发阶段方便快速设计SQL操作

15.8.2 使用Spring配置来创建内嵌数据库

如果你想要将内嵌的数据库实例作为Bean配置到Spring的ApplicationContext中,使用spring-jdbc命名空间下的embedded-database tag

<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数据库,并且在classpath下面配置schema.sql和test-data.sql资源。同时,作为一种最佳实践,内嵌数据库会被制定一个唯一生成的名字。内嵌数据库在Spring容器中作为javax.sql.DataSource Bean类型存在,并且能够被注入到所需的数据库访问对象中。

15.8.3 使用编程方式创建内嵌数据库

EmbeddedDatabaseBuilder提供了创建内嵌数据库的流式API。当你在独立的环境中或者是在独立的集成测试中可以使用这种方法创建一个内嵌数据库,下面是一个例子:

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

// perform actions against the db (EmbeddedDatabase extends javax.sql.DataSource)

db.shutdown()

更多支持的细节请参见:EmbeddedDatabaseBuilder 的JavaDoc

EmbeddedDatabaseBuilder 也可以使用Java Config类来创建内嵌数据库,下面是一个例子:

@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();
    }
}

15.8.4 选择内嵌数据库的类型

使用HSQL
spring支持HSQL 1.8.0及以上版本。HSQL是缺省默认内嵌数据库类型。如果显式指定HSQL,设置embedded-database Tag的type属性值为HSQL。如果使用builderAPI.调用EmbeddedDatabaseType.HSQL的setType(EmbeddedDatabaseType)方法。

使用H2
Spring也支持H2数据库。设置embedded-database tag的type类型值为H2来启用H2。如果你使用了builder API。调用setType(EmbeddedDatabaseType) 方法设置值为EmbeddedDatabaseType.H2。

使用Derby
Spring也支持 Apache Derby 10.5及以上版本,设置embedded-database tag的type属性值为BERBY来开启DERBY。如果你使用builder API,调用setType(EmbeddedDatabaseType)方法设置值为EmbeddedDatabaseType.DERBY.

15.8.5 使用内嵌数据库测试数据访问层逻辑

内嵌数据库提供了数据访问层代码的轻量级测试方案,下面是使用了内嵌数据库的数据访问层集成测试模板。使用这样的模板当内嵌数据库不需要在测试类中被重用时是有用的。不过,当你希望创建可以在test集中共享的内嵌数据库。考虑使用Spring TestContext测试框架,同时在Spring ApplicationContext中将内嵌数据库配置成一个Bean,具体参见15.8.2节, “使用Spring配置来创建内嵌数据库” 和15.8.3节, “使用编程方式创建内嵌数据库”.

public class DataAccessIntegrationTestTemplate {

	private EmbeddedDatabase db;

	@Before
	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( /* ... */ );
	}

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

}

15.8.6 生成内嵌数据库的唯一名字

开发团队在使用内嵌数据库时经常碰到的一个错误是:当他们的测试集想对同一个数据库创建额外的实例。这种错误在以下场景经常发生,XML配置文件或者@Configuration类用于创建内嵌数据库,并且相关的配置在同样的测试集的多个测试场景下都被用到(例如,在同一个JVM进程中)。例如,针对内嵌数据库的不同集成测试的ApplicationContext配置的区别只在当前哪个Bean定义是有效的。

这些错误的根源是Spring的EmbeddedDatabaseFactory工厂( XML命名空间和Java Config对象的EmbeddedDatabaseBuilder内部都用到了这个)会将内嵌数据库的名字默认设置成”testdb”.针对的场景,内嵌数据库通常设置成和Bean Id相同的名字。(例如,常用像“dataSource”的名字)。结果,接下来创建内嵌数据库的尝试都没创建一个新的数据库。相反,同样的JDBC链接URL被重用。创建内嵌数据的库的尝试往往从同一个配置返回了已存在的内嵌数据库实例。

为了解决这个问题Spring框架4.2 提供了生成内嵌数据库唯一名的支持。例如:

EmbeddedDatabaseFactory.setGenerateUniqueDatabaseName()
EmbeddedDatabaseBuilder.generateUniqueName()
<jdbc:embedded-database generate-name="true" …​ >

15.8.7 内嵌数据库扩展支持

Spring JDBC 内嵌数据库支持以下两种扩展支持:

实现EmbeddedDatabaseConfigurer支持新的内嵌数据库类型。

实现DataSourceFactory支持新的DataSource实现,例如管理内嵌数据库连接的连接池

欢迎贡献内部扩展给Spring社区,相关网址见:jira.spring.io.


书籍推荐