测试持久层(Dao 层)的难点在于:
- 单元测试必须执行隔离的代码;而持久层的代码需要和数据库进行交互。
- 单元测试必须快速运行;而访问数据库却相对较慢。
以上两个难点决定了嵌入式数据库(H2、HSQLDB、Derby 和 Java DB)的使用价值。嵌入式数据库使用场景较少,但是是配合 JUnit 测试持久层的最佳选择。
1. 基本使用
在 pom.xml 中添加 h2 数据库的依赖。如果是在非 Maven 项目中使用,则下载该 h2 的 .jar 包并加入项目的 classpath 中。
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
<scope>test</scope>
</dependency>
嵌入式数据库 H2 有多种使用模式,也可以将数据写入到磁盘上的文件中,但是大家更关注它的『内存模式』。即,将 database 和 table 建立在内存中。
private static final String DRIVER = "org.h2.Driver";
private static final String URL = "jdbc:h2:mem:scott;MODE=MYSQL;DB_CLOSE_DELAY=-1";
private static final String USERNAME = "sa";
private static final String PASSWORD = "";
-
<h3>jdbc:h2:mem:testdb</h3>
-
这是数据库 URL 的核心部分,其中 mem 就表示使用内存模式的 H2。H2 的各种不同的使用/运行模式,主要就体现在这个部分。
-
数据库名<small>(以及后续的用户名和密码)</small>并非重要部分,因为内存模式的数据库,在使用结束后会被清除,而且对它的使用无所谓用户名密码。
-
-
<h3>MODE=MYSQL</h3>
- H2 并不是唯一的嵌入式数据库,也不是唯一具有内存模式的嵌入式数据库,但是它是与 MySQL 语法最兼容的具有内存模式的嵌入式数据库<small>(虽然仍有些许特殊区别)</small>,这也是 JUnit中 首选 H2 的原因。
-
<h3>DB_CLOSE_DELAY=-1</h3>
-
默认情况下,H2 内存中的数据库是在最后一个<small>(或指定个数)</small>连接断开后关闭,这时就会删除数据库及其中数据。
-
设置为
-1
表示并非以连接数作为判断标准,而是持续保持数据库(即便没有连接),直到程序运行结束。
-
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
Statement stmt = conn.createStatement();
stmt.execute( "...");
除了正常的使用获得数据库 Connection 对象之外,H2 自带了连接池功能。
JdbcConnectionPool cp = JdbcConnectionPool.create(URL, USERNAME, PASSWORD);
Connection conn = cp.getConnection();
2. 初始化数据库
虽然可以在 JUnit 的 Before 方法中通过执行 SQL 语句的方式在 @Test 之前去初始化数据库环境,但是 H2 有一个更好的特性能实现数据库的初始化操作:Execute SQL on connection 。
H2 支持在连接上数据库的时候就执行 SQL 语句,相当于就初始化了数据库环境:
jdbc:h2:mem:<database>;...;INIT=RUNSCRIPT FROM '~/create.sql'
spring.datasource.url=jdbc:h2:mem:scott;MODE=MYSQL;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM './src/test/resources/create.sql'
H2 internally uses Unicode, and supports all character encoding systems and character sets supported by the virtual machine you use.
可以为测试类编写父类,并实现 @Before 方法,以方便/确保于在每个 @Test 方法前执行统一的初始化数据库(初始化测试环境)的代码。
public class DaoTestBase {
private static final String DRIVER = "org.h2.Driver";
private static final String URL = "jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1";
private static final String USERNAME = "sa";
private static final String PASSWORD = "";
@Before
public void setUp() throws URISyntaxException, ClassNotFoundException, SQLException {
String sqlPathName = getClass().getResource("/conf/sql/xxx.sql" ).toURI().toString().substring(6);
Class.forName(DRIVER);
Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
Statement stmt = conn.createStatement();
stmt.execute( "runscript from '" + sqlPathName + "'");
stmt.close();
conn.close();
}
}
三、H2 和 MySQL 的语法兼容性
所有的数据库都会有些小区别,即便是与 MySQL『最像』的数据库,H2 与 MySQL 仍有一些小区别:
- 不支持表级别的
Comment
(注释) - 插不支持入语句的出现
'
- H2
UNIQUE KEY
是数据库级别的,而非表级别 - 无法执行多个 Update 语句,即一次 update 只能插入一条数据。
- 列别名无法用于子查询
- 不支持
@:
语法
网友评论