Java单元测试实践-25.在本地使用H2数据库进行单元测试

Java单元测试实践-00.目录(9万多字文档+700多测试示例)
https://blog.csdn.net/a82514921/article/details/107969340

1. 前言

使用CI/CD可以实现按照要求自动执行单元测试,例如定期执行某个项目的单元测试。当在CI服务器执行单元测试时,CI服务器有可能无法访问数据库服务器。可以使用H2数据库,使执行单元测试时不依赖数据库服务器。

使用H2数据库执行单元测试的其他优势是,不会在数据库服务器中产生脏数据。

以下说明在单元测试中如何在本地使用H2数据库,摆脱对数据库服务器的依赖,帮助单元测试执行支持自动化。

2. H2数据库说明

参考 http://www.h2database.com/html/main.html

H2是一个开源的Java实现的SQL数据库,支持嵌入模式与服务器模式,支持内存数据库,提供了JDBC API。

2.1. 以嵌入模式使用H2数据库

参考 http://www.h2database.com/html/quickstart.html

以嵌入模式在应用中H2数据库,可以通过以下方式使用:

  • 将h2*.jar添加到classpath中(H2没有其他依赖);
  • JDBC驱动类使用:org.h2.Driver;
  • 数据库URL使用类型“jdbc:h2:~/test”的形式;
  • 新的数据库会被自动创建。

2.2. H2数据库与其他数据库的兼容性

参考 http://www.h2database.com/html/features.html#compatibility

所有数据库引擎的行为都有些不同。在可能的情况下,H2支持ANSI SQL标准,并尝试与其他数据库兼容。但是仍然存在一些差异:

在MySQL中,文本列默认不区分大小写,而在H2中,它们区分大小写。H2也可以支持列不区分大小写。

对于某些功能,H2数据库可以模拟特定数据库的行为。但是,这种方式仅实现了数据库之间差异的一小部分。

2.2.1. MySQL兼容模式

参考上述文档“MySQL Compatibility Mode”部分。

当需要使用MySQL模式时,可使用类似jdbc:h2:~/test;MODE=MySQL;DATABASE_TO_LOWER=TRUE的数据库URL,这种模式也能兼容MariaDB。当需要添加区分大小写的标识符时,可在URL添加;CASE_INSENSITIVE_IDENTIFIERS=TRUE。在创建数据库后不要修改DATABASE_TO_LOWER参数值。

  • 允许使用INDEX(…)或KEY(…)在CREATE TABLE语句中创建索引。例如:“create table test(id int primary key, name varchar(255), key idx_name(name));”;
  • 将浮点数转换为整数时,小数位数不会被截断,但是会四舍五入。
  • 将NULL与某个值连接会产生另一个值;
  • 在INSERT语句中支持ON DUPLICATE KEY UPDATE,由于这个特性,VALUES在某些情况下具有特殊的非标准含义;
  • INSERT IGNORE部分支持,如果未指定ON DUPLICATE KEY UPDATE,则可用于跳过具有重复key的行;
  • REPLACE INTO部分支持;
  • REGEXP_REPLACE()使用“\”作为向后引用,为了与MariaDB兼容;
  • Datetime值函数在命令中返回相同的值;
  • 0x文字被解析为二进制字符串文字;
  • 允许在DISTINCT查询的ORDER BY子句中使用不相关的表达式;
  • 部分支持某些MySQL特定的ALTER TABLE命令;
  • TRUNCATE TABLE会重新启动生成列的下一个值。

2.3. 数据库URL连接模式与设置

参考 http://h2database.com/html/features.html#database_url

在单元测试中使用H2数据库时,可能会使用其支持的嵌入(本地)模式连接、内存数据库、在连接时执行SQL等。

2.3.1. 嵌入(本地)模式连接

参考 http://www.h2database.com/html/features.html#embedded_databases

使用嵌入(本地)模式连接时,会在本地生成数据库文件。连接本地数据库的URL及示例如下所示:

jdbc:h2:[file:][<path>]<databaseName>
jdbc:h2:~/test
jdbc:h2:file:/data/sample
jdbc:h2:file:C:/data/sample (仅在Windows环境中使用)

前缀“file:”是可选的。如果不使用路径或仅使用相对路径,则将当前工作目录用作起点。路径和数据库名称是否区分大小写取决于操作系统,建议仅使用小写字母。数据库名称必须至少包含三个字符(File.createTempFile的限制)。数据库名称不得包含分号。使用/可以指向用户主目录,例如:“jdbc:h2:/test”。

使用H2数据库嵌入(本地)模式时,在测试结束后可对数据库数据进行检查。

2.3.2. 内存数据库

参考 http://www.h2database.com/html/features.html#in_memory_databases

连接内存数据库的URL及示例如下所示:

jdbc:h2:mem:(私有模式)
jdbc:h2:mem:<databaseName>(命名模式)
jdbc:h2:mem:test_mem(命名模式)

某些场景并不需要持久保存数据,或不需要持久保存数据的修改。这种情况下可以使用H2数据库的内存模式,数据不会被持久保存。

某些情况下,仅需要一个内存数据库的连接。这意味着要打开的数据库是私有的。在这种情况下,数据库URL为“jdbc:h2:mem:”。在同一虚拟机中打开两个连接意味着打开两个不同的数据库。

有时需要对同一个内存数据库的多个连接。在这种情况下,数据库URL必须包含一个名称。例如“jdbc:h2:mem:db1”。使用此URL访问相同的数据库仅在相同的虚拟机和类加载器环境中有效。

2.3.3. 在连接数据库时执行SQL

参考 http://www.h2database.com/html/features.html#execute_sql_on_connection

某些情况下,特别是对于内存数据库,当客户端连接到数据库时,可能需要自动执行DDL或DML命令,可通过INIT属性启用。可以将多个命令传递给INIT,需要使用分号及转义字符作为分隔符,如下所示:

String url = "jdbc:h2:mem:test;INIT=runscript from '~/create.sql'\\;runscript from '~/init.sql'";

双反斜杠仅在Java或properties文件中是必需的。在GUI或XML文件中,只需要一个反斜杠:

<property name="url" value=
"jdbc:h2:mem:test;INIT=create schema if not exists test\;runscript from '~/sql/init.sql'"
/>

当需要在INIT中指定反斜杠\时,也需要进行转义。假如需要指定Windows的路径,可使用正斜杠替代,会更简单。

2.3.3.1. RUNSCRIPT命令

参考 http://www.h2database.com/html/commands.html#runscript

RUNSCRIPT命令用于从文件执行SQL脚本。该脚本是一个包含SQL语句的文本文件。每个语句必须以“;”结尾。

可以使用URL代替文件名。若需要从classpath中读取流,可使用前缀’classpath:'。

RUNSCRIPT命令用法为“RUNSCRIPT FROM”,示例如下:

RUNSCRIPT FROM 'backup.sql'
RUNSCRIPT FROM 'classpath:/com/acme/test.sql'

2.4. H2数据库文件信息

参考 http://www.h2database.com/html/features.html#database_file_layout

当使用默认的MVStore引擎 http://www.h2database.com/html/mvstore.html#overview 时,持久保存的数据库会创建以下文件。

文件名格式文件名示例描述文件数量
(database).mv.dbtest.mv.db数据库文件,包含所有表的事务日志,索引和数据每个数据库1个文件
(database).newFiletest.newFile用于数据库压缩的临时文件,包含新的MVStore文件每个数据库0个或1个文件
(database).tempFiletest.tempFile用于数据库压缩的临时文件,包含临时MVStore文件每个数据库0个或1个文件

3. 支持H2的数据库管理工具

3.1. DBeaver(推荐使用)

推荐使用DBeaver对H2数据库进行管理,比H2本身提供的工具及SQuirreL SQL更好用。

3.1.1. 工具安装

可从以下地址下载DBeaver后安装:

https://dbeaver.io/download/

https://github.com/dbeaver/dbeaver/tags

3.1.2. 获取H2数据库驱动

可从本地的Maven/Gradle缓存目录中获取H2数据库驱动对应的jar包。

也可从Maven仓库下载H2数据库驱动,如2.1.214版本对应的地址为:https://repo1.maven.org/maven2/com/h2database/h2/2.1.214/h2-2.1.214.jar

如果需要下载H2数据库驱动的其他版本,可在以下目录中查找:https://repo1.maven.org/maven2/com/h2database/h2/

下载H2数据库驱动jar包后,可在DBeaver安装目录中创建目录(如“drivers”),将H2数据库驱动jar包保存在对应目录。

3.1.3. 配置H2数据库驱动

获取H2数据库驱动之后,需要在DBeaver中配置H2数据库驱动。

点击菜单“数据库”“驱动管理器”;

打开“驱动管理器”窗口后,输入“h2”(不区分大小写),点击“H2 Embedded V.2”选项,点击“编辑”按钮;

在这里插入图片描述

打开“编辑驱动”窗口后,点击“添加文件”按钮;

在这里插入图片描述

选择以上下载的H2数据库驱动jar包。

在这里插入图片描述

3.1.4. 创建H2数据库连接

在DBeaver中,点击“数据库”“新建数据库连接”菜单;

或者点击以下图标:

在这里插入图片描述

打开“连接到数据库”窗口后,点击“嵌入式”标签页,点击“H2 Embedded V.2”,点击“下一步”按钮;

在这里插入图片描述

在“路径”右边的输入框输入需要打开的H2数据库文件完整路径,或者点击“浏览”按钮后选择;

以上路径直接使用当前操作系统的文件路径格式即可,不需要增加其他参数,如下所示:

D:\desktop\h2db\jacg_h2db_rbc.mv.db

在这里插入图片描述

指定H2数据库文件路径后,点击“完成”按钮。

3.1.5. 打开H2数据库文件

在DBeaver中创建H2数据库连接后,会显示在左侧的列表中,点击列表前面的“>”图标可以打开H2数据库文件;

也可在列表中数据库对应项点击右键,点击“连接”菜单打开,还可进行编辑、删除、重命名等操作;

在这里插入图片描述

在列表中选中数据库对应项后,以下三个按钮分别可以用于连接数据库、断开/重新连接数据库、断开连接;

在这里插入图片描述

3.1.6. 打开SQL编辑器

在DBeaver中,打开“SQL 编辑器”“SQL 编辑器”菜单,或按“F3”键,可以打开SQL编辑器。

假如弹出了“Choose SQL script for”窗口,可以点击“New scrip”按钮,打开新的SQL编辑器窗口。

3.1.7. 执行SQL语句-需要指定schema及双引号

H2数据库使用的schema默认为大写,假如在创建H2数据库时指定的schema为小写,则执行SQL语句时,需要指定schema,且schema、表名、字段名需要使用半角双引号包含,如下所示:

SELECT "col", "col2" FROM "schema"."table_name" WHERE "col3" = '...'

在以上情况下,若使用常规方式的SQL语句,即不指定schema,schema、表名、字段名不使用半角双引号包含,则执行时会出错:

假如未指定schema,则会提示“SQL 错误 : Table “…” not found”;

假如有指定schema但未使用双引号,则会提示“SQL 错误 : Schema “…” not found”。

3.1.8. 执行SQL语句-常规方式(推荐使用)

为了能够对H2数据库执行常规方式的SQL语句,可以按照以下方式处理。

3.1.8.1. 前提条件-修改H2数据库文件路径格式

无论以下使用哪种实现方式,都需要先按照当前步骤处理。

在DBeaver左侧连接列表中数据库对应项点击右键,点击“编辑 连接”菜单(前文有截图),或按“F4”键,打开“连接…配置”窗口。

在默认打开的“连接设置”“主要”标签页,修改路径中指定的H2数据库文件路径,将文件后缀".mv.db"删除

例如H2数据库文件路径如下:

D:\desktop\h2db\jacg_h2db_rbc.mv.db

则修改后需要指定如下:

D:\desktop\h2db\jacg_h2db_rbc

在这里插入图片描述

3.1.8.2. 不同的实现方式

以下的“自动切换schema”方式,在打开H2数据库时会自动切换到指定的schema,可以执行常规方式的SQL语句;

“手动切换schema”方式,在打开H2数据库时不会自动切换到指定的schema,需要手动切换schema之后,才可以执行常规方式的SQL语句。

  • 自动切换schema-全局设置

在以上打开的“连接…配置”窗口中,点击“编辑驱动设置”按钮,打开“编辑驱动…”窗口;

通过前文所述,点击菜单“数据库”“驱动管理器”,打开“驱动管理器”窗口后,找到H2对应项,点击“编辑”按钮的方式,也可以打开“编辑驱动…”窗口;

在默认打开的“设置”标签页,在“URL 模板”对应的文本框中,在原有的“jdbc:h2:{file}”之后,增加以下内容:

;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=SET SCHEMA [schema]

以上schema根据实际情况替换为对应的schema名称,例如schema为“jacg”,则完整参数如下:

jdbc:h2:{file};DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=SET SCHEMA jacg

在这里插入图片描述

以上方式是DBeaver打开H2数据库文件时的全局配置,适用于H2数据库文件使用相同schema的场景。假如需要使用DBeaver打开的H2数据库文件的schema各不相同,则以上方式会失效

  • 自动切换schema-针对数据库设置

在“编辑驱动…”窗口“设置”标签页,在“URL 模板”对应的文本框中,在原有的内容之后,增加以下内容:

;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE

在“编辑驱动…”窗口“初始化”标签页,在“默认模式”中选择对应的schema:

在这里插入图片描述

以上方式针对DBeaver中特定的H2数据库,需要针对每个H2数据库连接进行配置,适用于H2数据库文件使用不同schema的场景

  • 手动切换schema-使用Dbeaver切换

在“编辑驱动…”窗口“设置”标签页,在“URL 模板”对应的文本框中,在原有的内容之后,增加以下内容:

;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE

在DBeaver左侧连接列表中,点击数据库对应项的“>”图标展开对应的schema列表,在需要切换的schema点击右键,点击“设为默认值”(当前的默认schema会以黑体形式显示)。

在这里插入图片描述

  • 手动切换schema-执行SQL语句

在“编辑驱动…”窗口“设置”标签页,在“URL 模板”对应的文本框中,在原有的内容之后,增加以下内容:

;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE

在DBeaver的SQL编辑器中执行以下SQL语句:

SET SCHEMA [schema];

例如schema为“jacg”,则需要执行的SQL语句如下:

SET SCHEMA jacg;

3.1.9. 使用图形化方式操作数据库

DBeaver也支持通过图形方式对数据库进行操作。

3.1.9.1. 操作数据库表

在DBeaver左侧连接列表中,点击数据库对应项的“>”图标展开,可以查看当前数据库中SCHEMA的相关表:

在这里插入图片描述

3.1.9.2. 复制SQL语句

在数据库表对应项点击右键,打开“生成 SQL”菜单,可以选择生成SELECT等SQL语句

在这里插入图片描述

例如点击“SELECT”菜单后出现以下SQL语句:

若不钩选“使用完全限定名称”,则显示的sql语句中不会在表名前显示SCHEMA;若钩选则显示的sql语句中会在表名前显示SCHEMA及"."。

点击“复制”按钮可以复制显示的sql语句

在这里插入图片描述

3.2. H2

3.2.1. 工具安装及使用

可从 http://www.h2database.com/html/download.html 下载H2数据库安装包。

安装后执行bin目录的h2.bat/h2w.bat/h2.sh脚本,会执行h2-xxx.jar中的org.h2.tools.Console类,之后会通过浏览器打开数据库管理界面,可管理H2及其他常见类型的数据库。

3.2.2. 打开H2数据库文件

启动H2数据库工具后,按照以下方式指定数据库信息并连接:

  • Saved Settings

选择“Generic H2 (Embedded)”。

  • Setting Name

选择“Generic H2 (Embedded)”。

  • Driver Class

org.h2.Driver

  • JDBC URL

格式为jdbc:h2:file:[数据库文件路径]

数据库文件路径中的后缀“.mv.db”不需要指定,例如数据库文件路径为E:\UnitTest\build\h2db.mv.db,则JDBC URL应为jdbc:h2:file:E:\UnitTest\build\h2db

  • User Name

不填。

  • Password

不填。

连接数据库配置如下所示:

在这里插入图片描述

连接数据库成功后,如下所示:

在这里插入图片描述

3.3. SQuirreL SQL

3.3.1. 工具安装及使用

参考 http://www.squirrelsql.org/ ,SQuirreL SQL Client是一个Java图形程序,允许查看JDBC兼容数据库的结构,浏览表中的数据,发出SQL命令等,支持多种常见类型的数据库,包括H2。

可从 http://squirrel-sql.sourceforge.net/#installation 下载SQuirreL SQL。

完成安装后,将H2驱动程序h2-xxx.jar拷贝至SQuirreL SQL安装目录的lib目录中。

H2驱动程序h2-xxx.jar可以从Maven中央仓库,或本地Maven/Gradle缓存,或H2安装目录的bin目录获取。

SQuirreL SQL安装目录的lib目录中指定H2驱动jar包版本,需要与生成H2数据库文件时使用的jar包版本一致,否则SQuirreL SQL有可能无法打开对应的H2数据库文件。

执行安装目录的squirrel-sql.bat/squirrel-sql.sh脚本,可以启动SQuirreL SQL

3.3.2. 打开H2数据库文件

启动SQuirreL SQL数据库工具后,打开“Aliases”标签页添加H2数据库信息,与以上使用H2时类似:

在这里插入图片描述

Name用于指定数据库别名,不影响使用

连接数据库成功后,如下所示:

在这里插入图片描述

打开Objects标签页,再打开数据库对应的schema

由于版本的区别,数据库表对应的菜单名字可能是“BASE TABLE”,而不是“TABLE”

点击连接数据库成功后出现的标签页的X按钮,可以关闭数据库连接

3.3.3. 执行SQL语句

在SQL标签页中,可以输入并执行SQL语句

由于版本的区别,在执行SQL语句时,可能需要在表名前指定schema,并将表名、schema与字段名均使用半角双引号包含,如下所示:

select * from "schema"."table_name" where "column1" = 'xxx' and "column1" <= 35;

4. 在单元测试中使用H2嵌入(本地)模式

4.1. 指定数据库建表语句

在单元测试中使用H2数据库嵌入(本地)模式,执行测试时需要指定数据库建表语句,使H2创建所需的数据库表。在数据库连接URL中通过INIT参数指定建表的SQL脚本文件。

4.2. 数据库连接参数

可参考示例项目,读取Groovy配置文件后对数据库连接参数进行了替换,对应unit_test_config.groovy文件的use_h2_file元素。

  • 驱动名称

org.h2.Driver

  • URL

在XML或properties文件中配置时,示例如下:

jdbc:h2:file:./build/h2db;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=RUNSCRIPT FROM 'classpath:sql/create_table.sql'

在Groovy配置文件中配置时,单引号'需要进行转义为\'

“file:./build/h2db”代表H2数据库文件生成在当前目录的build目录中,文件名为h2db。由于使用了相对路径的形式,“./”不能省略,否则会出现以下异常:

org.h2.jdbc.JdbcSQLNonTransientConnectionException: A file path that is implicitly relative to the current working directory is not allowed in the database URL "...". Use an absolute path, ~/name, ./name, or the baseDir setting instead. [90011-199]

MODE=MySQL;DATABASE_TO_LOWER=TRUECASE_INSENSITIVE_IDENTIFIERS=TRUE按照文档建议进行配置。

INIT=RUNSCRIPT FROM 'classpath:sql/create_table.sql'指定了连接数据库时会执行classpath中的sql/create_table.sql文件,对应示例项目中的src\test\resources\sql\create\_table.sql文件。

  • 用户名

空字符串。

  • 密码

空字符串。

4.3. 建表语句注意事项

  • 建表时判断是否存在

由于每次连接H2数据库时都会执行建表语句,因此需要判断当表不存在时才创建,使用“CREATE TABLE IF NOT EXISTS”。

  • 不支持的表选项

在旧版本H2中建表语句的表选项中,不支持ENGINE=InnoDBCOLLATE=utf8_bin、COLLATE utf8mb4_bin等选项,支持DEFAULT CHARSET=utf8COMMENT

在新版本H2(2.2.220)中,以上表选项都支持了

  • 不支持text字段类型

在旧版本H2中,不支持text字段类型

在新版本H2(2.2.220)中,支持text字段类型

  • 不支持前缀索引

H2不支持创建前缀索引,如下所示,需要删除以下示例中的“(255)”

INDEX idx_test(a, b, c(255))

4.4. 测试类注意事项

4.4.1. 测试类结束后关闭数据源

在测试类中,在测试方法均执行完毕后,需要关闭数据源,否则在后续的测试类执行数据库操作时可能会提示数据库文件被锁定,异常信息如下所示:

Caused by: org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database may be already in use: null. Possible solutions: close all other connection(s); use the server mode [90020-200]
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:622)
	at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
	at org.h2.message.DbException.get(DbException.java:194)
	...
Caused by: java.lang.IllegalStateException: The file is locked: nio:E:/UnitTest/build/h2db.mv.db [1.4.200/7]
	at org.h2.mvstore.DataUtils.newIllegalStateException(DataUtils.java:950)
	at org.h2.mvstore.FileStore.open(FileStore.java:166)
	at org.h2.mvstore.MVStore.<init>(MVStore.java:381)
	...
Caused by: java.nio.channels.OverlappingFileLockException
	at sun.nio.ch.SharedFileLockTable.checkList(FileLockTable.java:255)
	at sun.nio.ch.SharedFileLockTable.add(FileLockTable.java:152)
	at sun.nio.ch.FileChannelImpl.tryLock(FileChannelImpl.java:1108)
	at org.h2.store.fs.FileNio.tryLock(FilePathNio.java:121)
	...	

在示例项目中,测试基类TestMockBase、TestDbBase会在测试方法均执行完毕后关闭数据源(原因可参考前文),若测试类继承该类则不需要进行额外处理。

4.4.2. 测试类需要重新加载Spring Context

完成上述操作后,还需要使测试类执行前重新加载Spring Context,否则在使用数据源时可能会提示数据源已关闭,异常信息如下所示:

### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at Thu Feb 20 21:41:00 CST 2020
	at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:154)
	at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:141)
	...
Caused by: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at Thu Feb 20 21:41:00 CST 2020
	at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
	at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:81)
	at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
	...
Caused by: com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at Thu Feb 20 21:41:00 CST 2020
	at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1043)
	at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:941)
	at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:921)
	...

在示例项目中,测试基类TestMockBase使用了PowerMock,子类每次执行时都会重新加载Spring Context。

测试基类TestDbBase未使用PowerMock,在测试类级别增加了注解@DirtiesContext(classMode = BEFORE_CLASS),使执行该类前重新加载Spring Context。

4.5. 创建并设置SCHEMA

在单元测试配置参数的数据库连接URL中,需要通过INIT参数指定创建并设置SCHEMA,否则无法使用数据库工具打开生成的数据库文件,会提示“Schema “public” not found”。

在示例项目中,使用H2嵌入(本地)模式时,INIT参数指定的连接时执行的SQL脚本create_table.sql中,创建并设置SCHEMA为“TEST”,如下所示:

CREATE SCHEMA IF NOT EXISTS TEST;
SET SCHEMA TEST;

5. 在单元测试中使用H2内存数据库模式

在单元测试中使用H2内存数据库模式时,与使用嵌入(本地)模式相比,需要调整数据库连接URL参数

5.1. 数据库连接URL参数

可参考示例项目unit_test_config.groovy文件的use_h2_mem元素。

使用内存数据库私有模式的数据库连接URL参数示例如下:

jdbc:h2:mem:;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=RUNSCRIPT FROM 'classpath:sql/create_table.sql'

需要注意单引号在Groovy配置文件中需要转义。

6. H2与MySQL对比

6.1. 时间字段对比

小数秒精度默认值不同,可参考“JPA自动建表的时间类型”。

6.2. update语句返回行数

H2执行update语句返回的行数,与MySQL使用useAffectedRows=true时的行数相同,即返回受影响行数。

6.3. 关闭数据源

使用H2嵌入(本地)模式时,需要在测试类执行后关闭数据源,并重新加载Spring Context,避免数据库文件被锁定,或获取到已关闭的数据源连接。

6.4. H2与MySQL语句对比

6.4.1. insert ignore

对于insert ignore语句,H2与MySQL的执行操作及返回行数均一致。

可参考示例TestDatabaseInsertIgnore类。

  • 执行操作

插入时若出现错误会被忽略,例如主键或唯一索引出现重复数据。

  • 返回行数

返回行数为实际执行了插入的数据行数。

6.4.2. insert on duplicate key update

对于insert ignore语句,H2与MySQL的执行操作一致,H2与MySQL的jdbc连接参数使用useAffectedRows=true时的返回行数一致。

可参考示例TestDatabaseInsertUpdate类。

  • 执行操作

当不存在主键或唯一索引相同的数据时,执行插入操作。

当存在主键或唯一索引相同的数据时,执行更新操作。

  • 返回行数

返回行数如下(MySQL的jdbc连接参数使用useAffectedRows=true时的结果,若为false时返回的行数会不同):

  1. 当执行插入时,返回插入的行数;
  2. 当执行更新时,返回更新的行数*2;
  3. 当未执行插入或更新时,返回0;
  4. 批量处理时,返回以上记录总和。

6.4.3. replace into

对于replace into语句,H2与MySQL的执行操作一致,返回行数大部分情况一致,存在不同的情况。

可参考示例TestDatabaseReplaceInto类。

  • 执行操作

当不存在主键或唯一索引相同的数据时,执行插入操作。

当存在主键或唯一索引相同的数据时,先删除旧数据再插入。

  • 返回行数(MySQL文档说明)

返回受影响行数,即删除与插入的数据总和。

  • 返回行数(MySQL测试结果)
  1. 插入不存在主键或唯一索引相同的数据,返回对应记录行数;
  2. 插入存在主键或唯一索引、其他字段均相同的数据,返回对应记录行数;
  3. 插入存在主键或唯一索引相同,但其他字段不同的数据,返回对应记录行数*2;
  4. 批量处理时,返回以上记录总和。
  • 返回行数(H2测试结果)

H2数据库的replace into语句返回行数与MySQL存在区别,插入存在主键或唯一索引、其他字段均相同的数据,返回对应记录行数*2。

其他情况与MySQL返回行数一致。

6.4.4. SQL语句对比结果

SQL语句执行结果返回行数
insert ignore相同相同
insert on duplicate key update相同H2与MySQL使用useAffectedRows=true时的行数相同
replace into相同大部分情况相同
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值