Article

关系型数据库H2

更新于:2026-07-16

第一章:H2数据库概述

1.1 什么是H2数据库

概念名称说明注意事项
H2数据库一个用 Java 编写的开源关系型数据库管理系统(RDBMS),轻量、快速、支持标准 SQL。仅适用于 JVM 环境(如 Java、Kotlin、Scala)。
开源协议基于 Mozilla Public License 2.0(MPL 2.0)和 Eclipse Public License(EPL)。可免费用于商业和开源项目。
作者与维护由 Thomas Mueller 开发并维护,社区活跃。官方网站为 h2database.com。
核心目标提供嵌入式、内存或服务器模式的轻量级数据库,适合开发、测试和小型应用。不适合高并发、大规模生产 OLTP 场景。

1.2 H2的主要特性与优势

特性名称说明注意事项
纯 Java 实现无需本地依赖,跨平台运行,只需 JRE。启动快,部署简单。
多种运行模式支持嵌入式(in-process)、服务器(TCP/IP)、混合模式。可灵活切换,但连接字符串不同。
内存数据库支持可在内存中创建临时数据库,性能极高。进程结束后数据丢失,适合测试。
兼容主流 SQL 标准支持大部分 ANSI SQL-92/99/2003,兼容 MySQL、PostgreSQL、Oracle 语法模式。需通过 MODE 参数启用兼容模式。
Web 控制台内置基于浏览器的管理界面(H2 Console),便于调试和管理。默认端口 8082,需注意安全暴露。
加密与安全支持数据库文件加密(AES)、用户认证。加密会带来一定性能开销。
自动重连与恢复文件模式下支持崩溃后自动恢复(Write-Ahead Logging)。需启用 LOG=TRUE(默认开启)。
小体积JAR 包通常小于 2MB,适合嵌入到应用中。功能完整但资源占用极低。

1.3 H2的运行模式(嵌入式、服务器、混合)

模式名称说明连接字符串示例注意事项
嵌入式模式数据库与应用程序运行在同一 JVM 进程中,无网络开销,最快访问速度。jdbc:h2:~/test
jdbc:h2:mem:test
仅当前应用可访问;内存模式数据不持久。
服务器模式H2 作为独立 TCP 服务运行,多个客户端可通过网络连接。jdbc:h2:tcp://localhost/~/test需先启动 H2 TCP Server;支持多进程访问。
混合模式先以嵌入式方式启动,再启动 TCP 服务,允许本地和远程同时访问。嵌入式连接 + 启动 org.h2.tools.Server.createTcpServer()常用于开发调试,兼顾本地性能与远程访问。
内存模式数据仅存在于内存中,关闭连接后自动销毁(除非使用 named memory DB)。jdbc:h2:mem:testdb
jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1
使用 DB_CLOSE_DELAY=-1 可保持内存数据库存活。

1.4 适用场景与限制

类别说明注意事项
适用场景
单元/集成测试替代真实数据库进行快速、隔离的测试,无需外部依赖。Spring Boot 默认测试数据库。
原型开发快速搭建 MVP 或演示系统,无需配置复杂数据库。可配合 H2 Console 实时查看数据。
桌面应用嵌入到 Java 桌面程序中作为本地存储(如配置、日志、缓存)。文件模式可持久化。
教学与学习学习 SQL 和 JDBC 的理想工具,启动简单、文档齐全。支持多种 SQL 方言,便于对比学习。
限制与不足
并发能力有限虽支持多线程,但高并发写入性能不如 PostgreSQL、MySQL。不建议用于高负载生产环境。
功能精简缺少高级功能(如分区表、复杂复制、集群)。适合轻量级需求。
社区规模相比主流数据库,社区和第三方工具生态较小。遇到问题主要依赖官方文档。
数据规模单文件数据库,适合 GB 级以下数据;超大表性能下降明显。可通过索引优化,但非 OLAP 场景首选。

第二章:安装与配置

2.1 下载与安装H2

步骤名称操作细节注意事项
访问官网打开 H2 官方网站:https://www.h2database.com确保使用最新稳定版本(如 2.x)。
下载 JAR 包点击 “Download” 链接,下载 h2-*.jar 文件(通常为 h2-2.x.x.jar无需解压,单个 JAR 即可运行。
验证 Java 环境确保系统已安装 JRE 或 JDK(建议 Java 8+)执行 java -version 确认版本。
运行 H2在命令行执行:java -jar h2-*.jar默认会启动 Web Console 并打开浏览器。
添加到项目依赖Maven
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>

Gradle
implementation 'com.h2database:h2:2.2.224'
测试环境建议使用 testImplementation(Gradle)或 scope=test(Maven)。

2.2 启动H2控制台(Web Console)

步骤名称操作细节注意事项
命令行启动执行:java -cp h2-*.jar org.h2.tools.Server -web默认监听 http://localhost:8082
指定端口使用 -webPort 参数:
java -cp h2-*.jar org.h2.tools.Server -web -webPort 9090
避免端口冲突。
启动后自动打开浏览器默认行为;若未打开,手动访问 http://localhost:8082首次使用需设置 JDBC URL、用户名、密码。
控制台功能支持 SQL 执行、表结构查看、数据浏览、执行计划分析等。不建议在生产环境暴露 Web Console。
关闭控制台关闭命令行窗口,或按 Ctrl+C 终止进程。控制台独立于数据库引擎,关闭不影响嵌入式数据库。

2.3 配置H2运行环境(内存/文件模式)

模式类型配置方式示例路径/参数注意事项
内存模式JDBC URL 中使用 mem: 前缀jdbc:h2:mem:testdb数据在 JVM 生命周期内存在;默认关闭连接即销毁。
持久化内存添加 DB_CLOSE_DELAY=-1 保持内存数据库不随连接关闭而销毁jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1适用于单元测试中多个测试类共享内存库。
文件模式使用相对或绝对路径(无前缀或 file:jdbc:h2:~/mydb
jdbc:h2:/data/mydb
自动创建 .mv.db.trace.db 文件。
自动创建首次连接时若数据库不存在,H2 自动创建无需预先建库确保应用有写权限。
只读模式添加 ;ACCESS_MODE_DATA=rjdbc:h2:~/mydb;ACCESS_MODE_DATA=r防止意外写入,适合只读查询场景。
压缩与恢复默认启用 MVStore 引擎,支持崩溃恢复无需额外配置日志文件(.trace.db)可用于调试。

2.4 连接字符串详解

参数名称语法格式用途说明示例
基础路径jdbc:h2:[path]指定数据库位置(内存、文件、远程)jdbc:h2:mem:test
内存数据库jdbc:h2:mem:name创建命名内存数据库jdbc:h2:mem:myapp
用户目录jdbc:h2:~/name在用户主目录下创建数据库文件jdbc:h2:~/appdb
绝对路径jdbc:h2:/absolute/path/name指定完整文件路径jdbc:h2:/opt/data/mydb
TCP 服务器连接jdbc:h2:tcp://host[:port]/[path]连接远程 H2 服务器jdbc:h2:tcp://localhost/~/remotedb
兼容模式;MODE=MySQL / PostgreSQL / Oracle模拟其他数据库的 SQL 语法和函数jdbc:h2:mem:test;MODE=MySQL
自动重连;AUTO_RECONNECT=TRUE连接断开后自动重试默认 false
初始脚本;INIT=RUNSCRIPT FROM '...sql'连接时执行初始化 SQL 脚本;INIT=RUNSCRIPT FROM 'classpath:init.sql'
不自动提交;AUTOCOMMIT=FALSE关闭自动提交,需手动 COMMIT用于事务测试
忽略大小写;DATABASE_TO_UPPER=FALSE表名、列名保留原始大小写默认 true(转大写)
加密数据库;CIPHER=AES启用 AES 加密(需配合 FILEPASSWORD 和 PASSWORD)需额外配置密钥
延迟关闭;DB_CLOSE_DELAY=秒数 或 -1控制内存数据库在最后连接关闭后保留时间(-1 表示永不清除);DB_CLOSE_DELAY=-1
禁用跟踪日志;TRACE_LEVEL_FILE=0减少磁盘 I/O,提升性能默认为 2(记录详细日志)

注意: 多个参数用分号 ; 连接,顺序无关。例如:

jdbc:h2:mem:testdb;MODE=MySQL;DB_CLOSE_DELAY=-1;TRACE_LEVEL_FILE=0

第三章:SQL语法支持

3.1 DDL语句(CREATE, ALTER, DROP)

方法名称语法用途代码示例注意事项
CREATE TABLECREATE TABLE table_name (col_def [, ...]) [AS select]创建新表CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));支持自增列:ID INT AUTO_INCREMENT;主键可省略 CONSTRAINT 名称
CREATE TABLE ASCREATE TABLE new_table AS SELECT ...基于查询结果创建表CREATE TABLE active_users AS SELECT * FROM users WHERE active = 1;新表结构由 SELECT 推断,不复制索引或约束
ALTER TABLE ADD COLUMNALTER TABLE table_name ADD COLUMN col_def添加新列ALTER TABLE users ADD COLUMN email VARCHAR(100);列默认值可用 DEFAULT 指定
ALTER TABLE DROP COLUMNALTER TABLE table_name DROP COLUMN col_name删除列ALTER TABLE users DROP COLUMN temp_flag;不支持同时删除多个列(需逐条执行)
ALTER TABLE ALTER COLUMNALTER TABLE table_name ALTER COLUMN col_name {SET DATA TYPE | SET DEFAULT | DROP DEFAULT}修改列属性ALTER TABLE users ALTER COLUMN name SET DATA TYPE VARCHAR(100);不能直接重命名列(需用 RENAME COLUMN)
ALTER TABLE RENAME COLUMNALTER TABLE table_name RENAME COLUMN old TO new重命名列ALTER TABLE users RENAME COLUMN uname TO username;H2 2.x 起支持
DROP TABLEDROP TABLE [IF EXISTS] table_name [CASCADE | RESTRICT]删除表DROP TABLE IF EXISTS users CASCADE;CASCADE 自动删除依赖视图;默认 RESTRICT
CREATE INDEXCREATE [UNIQUE] INDEX index_name ON table (col [, ...])创建索引CREATE INDEX idx_user_name ON users(name);支持表达式索引:CREATE INDEX idx_upper ON users(UPPER(name))
DROP INDEXDROP INDEX [IF EXISTS] index_name删除索引DROP INDEX IF EXISTS idx_user_name;索引名在数据库范围内唯一
CREATE VIEWCREATE [OR REPLACE] VIEW view_name AS SELECT ...创建视图CREATE VIEW user_summary AS SELECT id, name FROM users;视图不可更新(除非简单投影)
DROP VIEWDROP VIEW [IF EXISTS] view_name删除视图DROP VIEW IF EXISTS user_summary;若被其他对象引用,需 CASCADE

3.2 DML语句(INSERT, UPDATE, DELETE)

方法名称语法用途代码示例注意事项
INSERT INTOINSERT INTO table VALUES (vals) | SELECT ...插入数据INSERT INTO users (name) VALUES ('Alice');支持批量插入:VALUES (...), (...), (...)
INSERT IGNOREINSERT IGNORE INTO ...忽略主键/唯一冲突(需兼容模式)INSERT IGNORE INTO users (id, name) VALUES (1, 'Bob');需启用 MODE=MySQL 才支持 IGNORE
UPDATEUPDATE table SET col = expr [, ...] [WHERE cond]更新记录UPDATE users SET name = 'Charlie' WHERE id = 1;无 WHERE 会更新全表,谨慎使用
UPDATE with JOINUPDATE table1 SET ... FROM table2 WHERE join_cond多表更新(H2 特有语法)UPDATE orders SET total = p.price FROM products p WHERE orders.pid = p.id;标准 SQL 不支持,H2 提供扩展
DELETEDELETE FROM table [WHERE cond]删除记录DELETE FROM users WHERE id = 1;无 WHERE 删除全表
TRUNCATE TABLETRUNCATE TABLE table_name快速清空表(重置自增计数器)TRUNCATE TABLE logs;比 DELETE FROM 更高效,不可回滚(自动 COMMIT)

3.3 查询语句(SELECT, JOIN, 子查询)

方法名称语法用途代码示例注意事项
SELECTSELECT [DISTINCT] cols FROM table [WHERE | GROUP BY | HAVING | ORDER BY | LIMIT]基本查询SELECT name FROM users WHERE age > 18 ORDER BY name LIMIT 10;LIMIT 支持 OFFSET:LIMIT 10 OFFSET 20
INNER JOINSELECT ... FROM t1 INNER JOIN t2 ON cond内连接SELECT u.name, o.amount FROM users u INNER JOIN orders o ON u.id = o.user_id;支持 USING(col) 简写
LEFT JOINSELECT ... FROM t1 LEFT JOIN t2 ON cond左外连接SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id;右表无匹配时填充 NULL
RIGHT JOINSELECT ... FROM t1 RIGHT JOIN t2 ON cond右外连接SELECT * FROM t1 RIGHT JOIN t2 ON t1.id = t2.ref_id;较少使用,可用 LEFT JOIN 重写
FULL OUTER JOINSELECT ... FROM t1 FULL JOIN t2 ON cond全外连接SELECT * FROM t1 FULL JOIN t2 ON t1.id = t2.id;H2 支持 FULL JOIN
子查询(标量)SELECT (SELECT MAX(age) FROM users) AS max_age;返回单值SELECT name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count FROM users u;性能可能较差,建议用 JOIN 优化
子查询(IN)SELECT * FROM t1 WHERE col IN (SELECT ...)成员判断SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);支持 NOT IN
EXISTS 子查询SELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE ...)存在性判断SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);比 IN 更高效,尤其对 NULL 安全
WITH (CTE)WITH cte AS (SELECT ...) SELECT * FROM cte;公共表表达式WITH active_users AS (SELECT * FROM users WHERE active = 1) SELECT * FROM active_users;支持递归 CTE(需指定 SEARCH 或 CYCLE)

3.4 内置函数与表达式

函数类别函数名称语法用途示例注意事项
字符串UPPER / LOWERUPPER(str)大小写转换SELECT UPPER(name) FROM users;支持多字节字符
字符串SUBSTRINGSUBSTRING(str FROM start FOR len)截取子串SELECT SUBSTRING(name FROM 1 FOR 3) FROM users;起始位置从 1 开始
字符串CONCATCONCAT(str1, str2, ...)字符串拼接SELECT CONCAT(first, ' ', last) FROM users;也支持 || 运算符
数值ROUND / CEIL / FLOORROUND(x, d)四舍五入等SELECT ROUND(price, 2) FROM products;d 为小数位数
日期时间NOW() / CURRENT_TIMESTAMPNOW()当前时间戳SELECT NOW();返回 TIMESTAMP 类型
日期时间DATEADDDATEADD(unit, amount, date)日期加减SELECT DATEADD('DAY', 7, NOW());unit'DAY', 'MONTH'
聚合COUNT / SUM / AVG / MIN / MAXCOUNT(*)聚合计算SELECT AVG(age) FROM users;支持 DISTINCT:COUNT(DISTINCT col)
条件CASE WHENCASE WHEN cond THEN val ELSE val END条件表达式SELECT CASE WHEN age < 18 THEN 'Minor' ELSE 'Adult' END FROM users;支持简单 CASE:CASE col WHEN 1 THEN ...
系统DATABASE()DATABASE()返回当前数据库名SELECT DATABASE();用于调试
序列NEXTVALNEXTVAL('seq_name')获取序列下一个值SELECT NEXTVAL('user_seq');需先 CREATE SEQUENCE

注: H2 还支持正则(REGEXP)、JSON(H2 2.0+)、窗口函数(ROW_NUMBER(), RANK())等高级功能。

3.5 事务控制(BEGIN, COMMIT, ROLLBACK)

方法名称语法用途代码示例注意事项
BEGIN / START TRANSACTIONBEGIN;START TRANSACTION;显式开始事务BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
默认自动提交(AUTOCOMMIT=TRUE),需关闭才生效
COMMITCOMMIT;提交当前事务COMMIT;持久化所有更改
ROLLBACKROLLBACK;回滚当前事务ROLLBACK;撤销所有未提交更改
SAVEPOINTSAVEPOINT name;设置保存点SAVEPOINT sp1;
...
ROLLBACK TO sp1;
支持部分回滚
ROLLBACK TOROLLBACK TO SAVEPOINT name;回滚到保存点ROLLBACK TO sp1;保存点之后的操作被撤销,之前仍保留
SET AUTOCOMMITSET AUTOCOMMIT {TRUE | FALSE};控制自动提交模式SET AUTOCOMMIT FALSE;JDBC 中通常通过 Connection.setAutoCommit() 控制
事务隔离级别SET LOCK_MODE {0|1|2|3};
SET TRANSACTION ISOLATION LEVEL ...
设置并发控制行为SET LOCK_MODE 0; -- 无锁(快照读)H2 默认使用 MVCC,LOCK_MODE 0 最高性能

注意: H2 使用多版本并发控制(MVCC),读操作通常不阻塞写操作。在文件模式下,事务日志确保崩溃恢复。


第四章:Java集成开发

4.1 使用JDBC连接H2

方法名称语法/操作用途代码示例注意事项
加载驱动(可选)Class.forName("org.h2.Driver")显式加载 JDBC 驱动(Java 6+ 通常无需)Class.forName("org.h2.Driver");现代 JVM 自动发现驱动,可省略
建立连接DriverManager.getConnection(url, user, password)获取数据库连接Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");默认用户名 sa,密码为空
执行查询Statement / PreparedStatement + executeQuery()执行 SELECTPreparedStatement ps = conn.prepareStatement("SELECT name FROM users WHERE id = ?");
ps.setInt(1, 1);
ResultSet rs = ps.executeQuery();
推荐使用 PreparedStatement 防 SQL 注入
执行更新executeUpdate()执行 INSERT/UPDATE/DELETEint rows = stmt.executeUpdate("INSERT INTO users (name) VALUES ('Alice')");返回受影响行数
关闭资源close() on Connection, Statement, ResultSet释放数据库资源rs.close(); stmt.close(); conn.close();建议使用 try-with-resources 自动关闭
内存数据库共享使用 named memory DB + DB_CLOSE_DELAY=-1多线程/多连接共享内存库"jdbc:h2:mem:shared;DB_CLOSE_DELAY=-1"否则每个连接创建独立内存实例

4.2 Spring Boot集成H2

配置项属性名(application.properties)用途示例值注意事项
数据库驱动spring.datasource.driver-class-name指定 H2 驱动类org.h2.Driver若依赖中只有 H2,可省略
JDBC URLspring.datasource.url指定 H2 连接地址jdbc:h2:mem:testdb
jdbc:h2:~/appdb
内存模式适合测试,文件模式可持久化
用户名spring.datasource.username数据库用户saH2 默认用户
密码spring.datasource.password数据库密码(空字符串)默认无密码
启用 Web Consolespring.h2.console.enabled开启 H2 控制台true开发环境使用,生产禁用
控制台路径spring.h2.console.path自定义控制台访问路径/h2-console默认为 /h2-console
DDL 自动建表spring.jpa.hibernate.ddl-autoJPA 自动建表策略create-drop(测试)
update(开发)
create-drop 在关闭时删除表
方言设置spring.jpa.database-platform指定 Hibernate 方言org.hibernate.dialect.H2DialectSpring Boot 2.7+ 可自动推断
日志 SQLspring.jpa.show-sql打印执行的 SQLtrue便于调试
初始化模式spring.sql.init.mode控制 schema.sql/data.sql 执行always / embedded / never默认 embedded(仅嵌入式数据库执行)

典型依赖(Maven):

<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <scope>runtime</scope>
</dependency>

4.3 单元测试中使用H2

场景配置方式用途示例注意事项
替换真实数据库使用 @DataJpaTest@SpringBootTest + H2 依赖隔离测试,避免污染生产数据@DataJpaTest
class UserRepositoryTest { ... }
Spring Boot 测试默认启用 H2(若在 classpath)
内存数据库隔离每个测试类使用唯一数据库名避免测试间数据干扰@TestPropertySource(properties = "spring.datasource.url=jdbc:h2:mem:test1")或使用随机名:jdbc:h2:mem:${random.uuid}
自动建表利用 schema.sql / data.sql初始化表结构和测试数据src/test/resources/schema.sql
src/test/resources/data.sql
文件需放在 test/resources
回滚事务使用 @Transactional on test class测试结束后自动回滚@DataJpaTest
@Transactional
class ServiceTest { ... }
默认行为,确保测试幂等
验证 SQL启用 SQL 日志调试生成的查询logging.level.org.hibernate.SQL=DEBUG可结合 AssertJ 或 TestEntityManager 验证

注意: H2 在单元测试中性能高、启动快,是 Spring Boot 官方推荐的测试数据库。

4.4 初始化脚本与数据填充

脚本类型文件名(默认)触发时机用途示例内容注意事项
Schema 初始化schema.sql应用启动时(在 JPA DDL 之后)创建表、索引、视图CREATE TABLE users (id INT PRIMARY KEY, name VARCHAR(50));若使用 JPA ddl-auto=create,可能重复建表
数据填充data.sqlschema.sql 执行后插入初始/测试数据INSERT INTO users VALUES (1, 'Admin');仅当 spring.sql.init.mode 允许时执行
自定义脚本路径spring.sql.init.schema-locations指定 schema 脚本位置多环境不同初始化classpath:db/init/schema-prod.sql支持多个路径,逗号分隔
自定义数据路径spring.sql.init.data-locations指定 data 脚本位置按 profile 加载数据classpath:db/data/test-data.sql
条件执行spring.sql.init.mode=always/embedded/never控制是否执行脚本仅在嵌入式 DB 执行embedded(默认)生产环境建议 never
编码指定spring.sql.init.encoding设置脚本文件编码避免中文乱码UTF-8默认为系统编码
分隔符spring.sql.init.separator自定义语句分隔符支持存储过程等;;默认为 ;,但不支持跨行注释内分号

执行顺序(Spring Boot):

  1. JPA/Hibernate 根据 ddl-auto 建表(如 create-drop
  2. 执行 schema.sql
  3. 执行 data.sql

提示: 在纯 JDBC 项目中,可通过 INIT 参数在连接字符串中执行脚本:

jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:init.sql'

第五章:高级功能

5.1 用户与权限管理

操作名称语法用途代码示例注意事项
创建用户CREATE USER user_name PASSWORD 'password' [ADMIN]新建数据库用户CREATE USER alice PASSWORD 'secret';ADMIN 用户拥有所有权限
修改密码ALTER USER user_name SET PASSWORD 'new_password'更新用户密码ALTER USER alice SET PASSWORD 'new123';需具备管理员权限
删除用户DROP USER user_name移除用户DROP USER alice;不能删除当前连接用户
授予权限GRANT {SELECT | INSERT | UPDATE | DELETE | ALL} ON table_name TO user_name赋予表级权限GRANT SELECT, INSERT ON users TO alice;支持 GRANT ALL PRIVILEGES
撤销权限REVOKE {privilege} ON table_name FROM user_name回收权限REVOKE DELETE ON users FROM alice;权限变更立即生效
查看权限SELECT * FROM INFORMATION_SCHEMA.USER_PRIVILEGES查询用户权限SELECT * FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES WHERE GRANTEE = 'ALICE';权限信息存储在系统表中
默认用户sa(System Administrator)初始超级用户密码默认为空,建议生产环境修改或禁用
连接认证使用用户名/密码连接控制访问入口jdbc:h2:~/mydb;USER=alice;PASSWORD=secret若未启用认证,任何用户可连接

注意: H2 的权限模型较简单,不支持角色(ROLE)或行级安全(RLS)。适用于轻量级多用户场景。

5.2 加密数据库

功能名称配置方式用途示例注意事项
启用文件加密在 JDBC URL 中指定 CIPHER=AES加密 .mv.db 数据文件jdbc:h2:~/securedb;CIPHER=AES必须同时提供 FILEPASSWORD 和 PASSWORD
设置文件密码FILEPASSWORD='file_pass'用于加密数据库文件的密钥jdbc:h2:~/securedb;CIPHER=AES;FILEPASSWORD='mypass'文件密码 ≠ 用户密码
设置用户密码PASSWORD='user_pass'数据库用户登录密码jdbc:h2:~/securedb;CIPHER=AES;FILEPASSWORD='fp';PASSWORD='up'用户密码用于认证,文件密码用于解密存储
加密算法仅支持 AES(128 位)对称加密保护数据依赖 Java JCE 策略(默认支持)
性能影响I/O 加解密开销安全性 vs 性能权衡写入/读取速度下降约 10%~30%
备份限制加密数据库无法直接读取防止未授权访问必须通过 H2 使用正确密码打开
兼容性仅 H2 可读不与其他数据库兼容不能将加密 H2 文件导入 MySQL 等

完整连接示例:

jdbc:h2:~/mydb;CIPHER=AES;FILEPASSWORD='file123';PASSWORD='user123'

警告: 若丢失 FILEPASSWORD,数据将永久不可恢复!

操作名称语法用途代码示例注意事项
创建全文索引CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init";
CALL FTL_INIT();
CALL FTL_CREATE_INDEX('PUBLIC', 'TABLE_NAME', 'COL1,COL2');
基于 Lucene 构建全文索引CALL FTL_CREATE_INDEX('PUBLIC', 'ARTICLES', 'TITLE,CONTENT');首次需初始化 FTL 模块
执行全文搜索SELECT * FROM FTL_SEARCH('query', 0, 0) T JOIN ARTICLES A ON T.TABLE_SCHEMA='PUBLIC' AND T.TABLE_NAME='ARTICLES' AND T.KEYS[1]=A.ID;搜索包含关键词的记录SELECT * FROM FTL_SEARCH('database performance', 0, 0) ...返回匹配文档的主键
删除全文索引CALL FTL_DROP_ALL();FTL_DROP_INDEX(...)清理全文索引CALL FTL_DROP_INDEX('PUBLIC', 'ARTICLES');索引独立于表,需手动维护
搜索语法支持 Lucene 查询语法高级搜索(布尔、短语等)"fast database" OR (performance AND tuning)关键词默认分词(空格、标点)
自动同步插入/更新/删除时自动更新索引保持索引一致性由触发器实现,对性能有轻微影响
依赖要求h2-*.jar 内置 Lucene 模块全文功能非默认启用H2 2.x 已内置,无需额外 JAR

建议: 可创建视图或存储函数封装 FTL_SEARCH 的复杂 JOIN。

5.4 自定义函数与存储过程

类型方法用途代码示例注意事项
创建 Java 函数(别名)CREATE ALIAS func_name FOR "ClassName.methodName";将 Java 静态方法映射为 SQL 函数CREATE ALIAS SPLIT FOR "java.lang.String.split";方法必须是 public static
调用自定义函数SELECT func_name(args) FROM ...在 SQL 中使用SELECT SPLIT('a,b,c', ',') FROM DUAL;返回值需为基本类型或数组
创建存储过程(Java)CREATE ALIAS proc_name AS $$ ... $$;实现复杂逻辑见下方示例使用 $$...$$ 包裹 Java 代码(H2 特有)
调用存储过程CALL proc_name(args);执行过程CALL LOG_MESSAGE('User logged in');无返回值,用于副作用操作
参数类型支持 Java 基本类型、String、Connection 等传递上下文void myFunc(String s, int i, Connection conn)Connection 可用于执行内部 SQL
安全限制默认禁止动态编译(需设置 ALLOW_LITERALS=ALL防止代码注入jdbc:h2:mem:test;ALLOW_LITERALS=ALL生产环境慎用,建议预编译函数
删除别名DROP ALIAS func_name;移除自定义函数/过程DROP ALIAS SPLIT;无依赖检查

示例:自定义字符串反转函数

CREATE ALIAS REVERSE_STRING AS $$
String reverse(String s) {
  return new StringBuilder(s).reverse().toString();
}
$$;
SELECT REVERSE_STRING('hello'); -- 返回 'olleh'

注意: 自定义函数在嵌入式模式下有效;服务器模式需确保类在服务端 classpath。


第六章:性能与调优

6.1 内存 vs 文件模式性能对比

对比维度内存模式(mem:)文件模式(~/ 或 /path)注意事项
数据持久性❌ 进程结束即丢失(除非 DB_CLOSE_DELAY=-1✅ 数据写入 .mv.db 文件,持久保存内存模式仅适合临时数据
读写速度⚡ 极快(无磁盘 I/O)🐢 受磁盘 I/O 限制,但使用 MVStore 引擎优化内存模式吞吐量高 5~10 倍
并发支持✅ 支持多线程读写(MVCC)✅ 同样支持 MVCC,并可被多个进程通过 TCP 访问文件模式需注意文件锁
内存占用🔺 全部数据驻留堆内存,大表易导致 OOM🔻 仅缓存热点数据,适合 GB 级数据内存模式不适合大数据集
启动时间⚡ 几毫秒🐢 首次启动需加载文件(随数据量增长)文件模式冷启动较慢
适用场景单元测试、原型验证、缓存中间结果桌面应用、小型服务、需持久化的嵌入式系统生产环境优先选文件模式
崩溃恢复❌ 无恢复能力✅ 通过 WAL(Write-Ahead Log)支持崩溃后自动恢复默认启用日志(.trace.db
备份便利性❌ 无法直接备份✅ 可复制 .mv.db 文件(需关闭或使用 BACKUP SQL)热备份需谨慎

建议: 开发/测试用内存模式,生产用文件模式;避免在内存模式下存储 >100MB 数据。

6.2 索引优化

操作名称语法/方法用途示例注意事项
创建普通索引CREATE INDEX idx_name ON table(col)加速 WHERE、ORDER BY 查询CREATE INDEX idx_user_email ON users(email);高选择性列优先建索引
创建唯一索引CREATE UNIQUE INDEX idx_name ON table(col)保证列值唯一 + 提升查询速度CREATE UNIQUE INDEX idx_user_login ON users(login);插入重复值会报错
创建复合索引CREATE INDEX idx_name ON table(col1, col2)优化多列条件查询CREATE INDEX idx_order_status_date ON orders(status, created_at);列顺序影响效果(最左前缀)
表达式索引CREATE INDEX idx_name ON table(UPPER(col))加速函数化查询CREATE INDEX idx_upper_name ON users(UPPER(name));查询时需使用相同表达式
查看索引SELECT * FROM INFORMATION_SCHEMA.INDEXES WHERE TABLE_NAME='USERS'诊断现有索引可结合 EXPLAIN 分析是否命中
删除无效索引DROP INDEX idx_name移除冗余或低效索引DROP INDEX idx_unused;索引增加写开销,定期清理
主键自动索引定义 PRIMARY KEY 时自动创建主键查询极快CREATE TABLE t(id INT PRIMARY KEY);无需手动创建
覆盖索引(Covering)索引包含 SELECT 所有字段避免回表,提升性能CREATE INDEX idx_cover ON orders(user_id, total, status);
SELECT total, status FROM orders WHERE user_id = 1;
H2 支持但需显式包含列
索引统计更新ANALYZE [TABLE table_name]更新表统计信息,帮助优化器决策ANALYZE TABLE users;大量数据变更后建议执行

反模式:

  • 对低区分度列(如 gender)建索引
  • 在频繁 UPDATE/DELETE 的表上建过多索引

6.3 查询执行计划分析

方法名称语法用途示例注意事项
EXPLAINEXPLAIN [PLAN FOR] SELECT ...显示逻辑执行计划(优化器输出)EXPLAIN SELECT * FROM users WHERE email = 'mailto:a@b.com';输出为树状结构,含操作类型
EXPLAIN ANALYZEEXPLAIN ANALYZE SELECT ...显示实际执行计划 + 耗时(H2 2.0+)EXPLAIN ANALYZE SELECT COUNT(*) FROM logs;需启用 TRACE_LEVEL_SYSTEM_OUT=3 查看详细日志
执行计划字段OPERATION, CARDINALITY, COST解读计划关键指标CARDINALITY 为预估行数,COST 为相对代价
常见操作类型TABLE_SCAN, INDEX_SCAN, JOIN, AGGREGATE识别性能瓶颈若出现 TABLE_SCAN 而应走索引,需检查索引或统计信息全表扫描在小表可接受
强制索引提示/*+ INDEX(table index_name) */手动指定索引(调试用)SELECT /*+ INDEX(users idx_email) */ * FROM users WHERE email = 'x';仅用于测试,不推荐生产使用
查看优化器决策SET TRACE_LEVEL_SYSTEM_OUT=3输出优化器详细日志到控制台日志量大,仅临时开启

典型优化路径:

  1. 用 EXPLAIN 确认是否走索引
  2. 若未命中,检查 WHERE 条件是否匹配索引列及顺序
  3. 执行 ANALYZE 更新统计信息
  4. 考虑添加复合索引或表达式索引

6.4 缓存机制

缓存类型配置参数作用范围默认值调优建议
页面缓存(Page Cache)CACHE_SIZE (KB)缓存数据页(.mv.db 块)16384(16MB)大内存机器可增至 65536(64MB)
结果集缓存不直接支持应用层自行缓存(如 Caffeine)
执行计划缓存内部自动缓存 PreparedStatement相同 SQL 重用计划✅ 启用使用参数化查询以复用计划
查询结果缓存❌ 不支持需依赖外部缓存(Redis/Ehcache)
设置缓存大小jdbc:h2:...;CACHE_SIZE=32768启动时指定单位 KB,最大约 64MB(受 JVM 堆限制)
清空缓存SHUTDOWN DEFRAG重启时压缩并清缓存可减少文件碎片,但非运行时操作
缓存监控无直接视图通过性能压测间接评估

重要说明:

  • H2 的缓存主要用于磁盘 I/O 优化,非查询结果缓存。
  • 内存模式下缓存意义不大(数据已在内存)。
  • 过大 CACHE_SIZE 会挤占应用堆内存,需平衡。

设置示例:jdbc:h2:~/mydb;CACHE_SIZE=65536 → 分配 64MB 缓存


第七章:工具与运维

7.1 H2 Console使用指南

功能名称操作方式 / 路径用途说明注意事项
启动 Web 控制台命令行执行:
java -jar h2-*.jar

java -cp h2-*.jar org.h2.tools.Server -web
启动内嵌 Web 服务器并自动打开浏览器默认端口 8082;可加 -webPort 9090 修改
访问地址http://localhost:8082手动访问控制台(若未自动打开)需确保端口未被占用
连接配置在登录页填写:
- JDBC URL
- 用户名(默认 sa)
- 密码(默认空)
连接到目标 H2 数据库支持内存、文件、远程 TCP 模式
SQL 执行区页面中部文本框编写并执行任意 SQL 语句支持多语句(用 ; 分隔)
结果展示执行后下方表格显示查看查询结果或操作影响行数可导出为 CSV
表结构浏览左侧 “Tables” 树形菜单 → 点击表名查看列定义、主键、索引支持直接编辑数据(双击单元格)
执行计划查看勾选 “Explain” 复选框后执行 SELECT显示查询的执行计划有助于性能分析
自动补全输入时按 Ctrl+Space触发 SQL 关键字/表名列名提示提升编写效率
安全建议控制台仅用于开发/测试环境❌ 切勿在生产环境暴露 8082 端口

7.2 备份与恢复

操作名称方法 / 语法用途示例注意事项
SQL 备份(逻辑)BACKUP TO 'file.zip'将当前数据库导出为 ZIP 格式逻辑备份BACKUP TO '/backup/mydb.zip';可跨平台/版本恢复;包含 schema + data
SQL 恢复(逻辑)RUNSCRIPT FROM 'file.zip'从 ZIP 备份重建数据库RUNSCRIPT FROM '/backup/mydb.zip';需新建空库后执行
文件复制(物理)直接复制 .mv.db 文件快速物理备份(需数据库关闭)cp mydb.mv.db mydb_backup.mv.db若数据库运行中,可能损坏(需 SHUTDOWN FIRST)
热备份(运行中)使用 BACKUP SQL(推荐)无需停机,安全一致CALL BACKUP TO 'hot.zip';H2 2.x 支持在线备份
自动备份脚本定时任务调用 JDBC 执行 BACKUP实现定期自动备份Linux cron: 0 2 * * * java -cp h2.jar BackupTool需编写简单 Java 工具类
恢复到新实例.mv.db 文件放入新路径,启动连接快速还原物理备份jdbc:h2:/restore/mydb确保 H2 版本兼容
增量备份❌ 不支持H2 无 WAL 归档机制,仅支持全量

最佳实践:

  • 开发/测试:用 BACKUP TO + 版本控制
  • 生产嵌入式应用:每日定时 BACKUP TO ZIP + 保留多份
  • 恢复前验证 ZIP 内容(可用 unzip -l 查看)

7.3 日志与调试

日志类型配置方式 / 文件名用途默认行为调优建议
数据库跟踪日志自动生成 .trace.db 文件记录错误、警告、内部操作✅ 启用(TRACE_LEVEL_FILE=2可设为 0 关闭以提升性能
控制台输出日志设置 TRACE_LEVEL_SYSTEM_OUT=1~3将日志打印到标准输出(如 IDE 控制台)0(关闭)调试时设为 23
SQL 语句日志应用层配置(如 Spring Boot)打印执行的 SQL关闭logging.level.org.hibernate.SQL=DEBUG
日志级别含义0=OFF, 1=ERROR, 2=INFO, 3=DEBUG控制日志详细程度FILE=2, SYSTEM_OUT=0高级别日志影响性能
清理日志文件删除 .trace.db 或设置 TRACE_MAX_FILE_SIZE防止日志无限增长无限制可设 TRACE_MAX_FILE_SIZE=10(MB)
调试连接问题检查 .trace.db 或开启 SYSTEM_OUT诊断连接失败、权限错误等常见错误:文件锁冲突、路径权限不足
慢查询日志❌ H2 无内置慢查询日志需应用层自行记录执行时间

连接字符串示例(开启详细日志):

jdbc:h2:~/mydb;TRACE_LEVEL_FILE=3;TRACE_LEVEL_SYSTEM_OUT=3;TRACE_MAX_FILE_SIZE=20

7.4 版本升级与兼容性

事项说明操作建议注意事项
向后兼容性H2 通常保证向后兼容(新版本可读旧版 .mv.db直接替换 JAR 即可极少数大版本(如 1.x → 2.x)需迁移
跨大版本升级(1.x→2.x)MVStore 引擎变更,旧格式不兼容1. 用旧版 H2 导出为 SQL 脚本
2. 用新版 H2 执行脚本重建
不可直接替换 .mv.db 文件
导出为 SQL 脚本使用 H2 Console 或命令行工具CALL SCRIPT TO 'dump.sql';包含 schema + data,文本格式通用
自动升级(小版本)如 2.1.214 → 2.2.224替换 JAR,重启应用通常无缝,建议先测试
兼容模式变更新版可能调整 MySQL/PostgreSQL 模拟行为检查 RELEASE NOTES如分页语法 LIMIT/OFFSET 行为微调
JDBC 驱动类名始终为 org.h2.Driver无需修改代码驱动 API 保持稳定
降级风险新版创建的数据库不能被旧版读取❌ 禁止降级升级前务必备份
检查版本SELECT H2VERSION();确认当前运行版本或通过 JAR MANIFEST.MF 查看

升级流程(安全):

  1. 备份数据库(BACKUP TO 或复制 .mv.db
  2. 在测试环境验证新版本功能与性能
  3. 若跨大版本,使用 SCRIPT/BACKUP 迁移
  4. 更新生产依赖,重启应用

官方兼容声明: H2 项目承诺同一主版本内(如 2.x)完全兼容;主版本升级需手动迁移。


第八章:与其他数据库对比

8.1 与SQLite对比

对比维度H2 数据库SQLite说明
语言实现纯 JavaC 语言H2 仅适用于 JVM 环境;SQLite 可嵌入任何语言
运行模式嵌入式、服务器、内存仅嵌入式(无独立服务进程)H2 支持多进程通过 TCP 访问;SQLite 需文件共享
并发写入✅ 多线程写(MVCC)❌ 全局写锁(同一时间仅一个写操作)H2 更适合高并发写场景
SQL 标准支持高度兼容 ANSI SQL + MySQL/PG/Oracle 模式基本 SQL-92,部分扩展H2 语法更丰富,函数更全
类型系统强类型(INT, VARCHAR, TIMESTAMP 等)动态类型(基于值而非列定义)H2 更接近传统 RDBMS
内存数据库✅ 原生支持(jdbc:h2:mem:...✅ 支持(:memory:两者均适合单元测试
文件格式单一 .mv.db 文件单一 .db 文件均便于分发
加密✅ 内置 AES 加密(CIPHER=AES❌ 官方不支持(需 SEE 商业版或第三方扩展)H2 安全性更强
Web 控制台✅ 内置 H2 Console❌ 无(需第三方工具如 DB Browser)H2 开发调试更便捷
适用场景Java 应用、Spring Boot 测试、桌面软件移动 App(Android/iOS)、小型 C/C++ 工具技术栈决定选型

8.2 与HSQLDB对比

对比维度H2 数据库HSQLDB(HyperSQL)说明
性能(内存模式)⚡ 更快(尤其写入和复杂查询)🐢 较慢H2 优化更激进
启动速度⚡ 极快(<50ms)🐢 较慢(>100ms)H2 更适合频繁启停的测试
SQL 兼容性✅ 支持 MySQL/PostgreSQL/Oracle 模式⚠️ 有限兼容(主要 ANSI SQL)H2 更易迁移遗留 SQL
全文搜索✅ 内置 Lucene 集成❌ 不支持H2 功能更全面
事务隔离MVCC(快照读)传统锁机制H2 读写并发更高
社区活跃度✅ 高(GitHub 活跃,Spring Boot 默认测试库)⚠️ 低(更新缓慢)H2 生态更现代
文件大小~2MB JAR~1.5MB JAR两者均轻量
自定义函数✅ 支持 Java 方法映射✅ 支持能力相当
默认用户sa(空密码)SA(空密码)安全配置类似
适用场景现代 Java 项目首选老旧 Java 项目、特定合规要求新项目推荐 H2

8.3 与Derby对比

对比维度H2 数据库Apache Derby说明
所属生态独立开源项目Apache 基金会(曾为 IBM Cloudscape)Derby 企业背景强
部署方式单 JAR(~2MB)多 JAR(需 derby.jar + derbytools.jar 等)H2 更简洁
启动复杂度⚡ 极简(java -jar h2.jar🐢 需启动 NetworkServerControl 或嵌入式初始化H2 上手更快
内存模式✅ 原生支持⚠️ 支持但配置繁琐(jdbc:derby:memory:db;create=trueH2 更直观
SQL 方言灵活(多兼容模式)严格 ANSI SQLH2 更灵活
工具支持✅ 内置 Web Console❌ 依赖 ij 命令行或第三方工具H2 开发体验更好
性能⚡ 优于 Derby(尤其在内存模式)🐢 较慢多项基准测试证实
社区与更新✅ 活跃(年更多次)⚠️ 缓慢(年更 0~1 次)Derby 近年趋于维护状态
许可证MPL 2.0 / EPLApache License 2.0均可用于商业项目
适用场景快速开发、测试、轻量应用需 Apache 生态集成、特定企业标准新项目优先 H2

8.4 在测试环境中的替代方案选择

方案优点缺点推荐场景
H2(内存模式)✅ 启动极快
✅ Spring Boot 默认
✅ 功能完整
❌ 与生产 DB(如 MySQL)存在方言差异大多数 Spring Boot 单元/集成测试
Testcontainers + 真实 DB✅ 100% 生产一致性
✅ 支持复杂特性(存储过程、分区等)
❌ 启动慢(秒级)
❌ 依赖 Docker
需要完全一致性的集成测试、E2E 测试
H2(文件模式)✅ 数据持久化
✅ 可调试
❌ 仍存在方言差异
❌ 非真实 DB 行为
需跨测试类共享状态的场景
SQLite(JDBC)✅ 轻量
✅ 文件即数据库
❌ 写并发差
❌ 类型系统弱
❌ 无兼容模式
非 Java 项目或简单只读测试
HSQLDB✅ ANSI SQL 严格❌ 性能较差
❌ 社区停滞
遗留系统兼容测试
Derby✅ Apache 生态❌ 配置复杂
❌ 启动慢
特定企业合规要求

选型决策树:

  1. 是否要求 100% 生产一致性? → 是:用 Testcontainers + 真实数据库 → 否:进入下一步
  2. 是否使用 Spring Boot? → 是:默认用 H2 内存模式(最快最简)
  3. 是否涉及复杂 SQL(如窗口函数、JSON)? → 是:检查 H2 是否支持,否则考虑 Testcontainers
  4. 是否需多进程共享测试数据? → 是:用 H2 服务器模式或文件模式

最佳实践:

  • 单元测试:H2 内存模式(@DataJpaTest
  • 集成测试:H2(若逻辑简单)或 Testcontainers(若需真实行为)
  • 避免在测试中使用与生产差异过大的方言(如 H2 的 MERGE 语法 vs MySQL 的 ON DUPLICATE KEY)