当前位置: 首页 > news >正文

Java操作H2数据库实战

目录

第一部分:H2数据库系统介绍

1.1 H2数据库概述

1.2 核心特性与优势

1.3 典型应用场景

1.4 架构与存储机制

第二部分:Java操作H2数据库

2.1 环境配置与准备

2.2 数据库连接管理

2.3 完整CRUD示例

2.4 测试程序


第一部分:H2数据库系统介绍

1.1 H2数据库概述

H2数据库是一个开源的关系型数据库管理系统(RDBMS),完全由Java语言编写。它由Thomas Mueller创建,首个版本发布于2005年,旨在成为高性能、轻量级的数据库解决方案。与传统数据库相比,H2最显著的特点是零配置部署嵌入式运行能力,这使得它成为开发、测试和小型应用部署的理想选择。

1.2 核心特性与优势
  1. 多模式运行

    • 嵌入式模式:作为应用进程的一部分运行,无需独立进程

    • 内存模式:数据完全存储在RAM中,速度极快

    • 服务端模式:独立TCP服务,支持多客户端连接

    • 混合模式:同时支持嵌入式和远程连接

  2. 性能表现

    • 内存模式下操作速度可比传统数据库快10-100倍

    • 支持高效的索引算法(B-tree、树状索引)

    • 批量操作优化,支持预编译语句缓存

  3. 兼容性优势

    • 支持标准SQL

    • 提供兼容模式(Oracle、SQL Server、MySQL等方言)

    • JDBC API完全兼容(JDBC规范)

  4. 安全特性

    • 强加密支持(AES-128/AES-256)

    • 基于角色的访问控制

    • 数据库文件加密

1.3 典型应用场景
  1. 单元测试与集成测试:内存数据库特性完美支持自动化测试

  2. 嵌入式应用:桌面软件、移动应用、IoT设备等资源受限环境

  3. 快速原型开发:无需复杂环境配置即可启动开发

  4. 缓存层实现:作为应用和主数据库之间的缓冲层

  5. 临时数据分析:快速处理CSV/Excel导入的临时数据集

1.4 架构与存储机制

H2采用模块化架构设计,主要包含:

  • SQL解析器:将SQL转换为内部执行计划

  • 优化器:基于成本的查询优化(CBO)

  • 事务管理器:ACID事务支持(MVCC实现)

  • 存储引擎

    • MVStore(默认):基于日志结构合并树(LSM-tree)

    • PageStore:传统B-tree实现(适用于只读场景)

存储文件格式:

  • .mv.db:MVStore格式数据文件

  • .h2.db:PageStore格式数据文件

  • .lock.db:数据库锁文件

  • .trace.db:操作日志文件

第二部分:Java操作H2数据库

2.1 环境配置与准备

Maven依赖配置

<dependencies><!-- H2数据库核心依赖 --><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><version>2.2.224</version></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.1.0</version> </dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency>
</dependencies>
2.2 数据库连接管理

H2支持多种连接模式,通过不同URL格式区分:

// 内存数据库(应用退出后数据消失)
String memUrl = "jdbc:h2:mem:testdb";
​
// 嵌入式文件数据库(数据持久化到磁盘)
String fileUrl = "jdbc:h2:file:~/h2data/mydb";
​
// TCP服务模式(独立进程)
String serverUrl = "jdbc:h2:tcp://localhost:9092/~/testdb";
​
// 带认证的连接示例
String url = "jdbc:h2:~/production;USER=admin;PASSWORD=secret123";

连接池配置(使用HikariCP实现):

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
​
public class H2Pool {
​private static final HikariDataSource dataSource;
​static {HikariConfig config = new HikariConfig();config.setJdbcUrl("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1");config.setUsername("sa");config.setPassword("");
​//config.addDataSourceProperty("cachePrepStmts", "true");config.addDataSourceProperty("prepStmtCacheSize", "250");config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");config.addDataSourceProperty("useServerPrepStmts", "true");
​config.setMaximumPoolSize(10);
​dataSource = new HikariDataSource(config);}
​public static HikariDataSource getDataSource() {return dataSource;}
​
}
2.3 完整CRUD示例
import lombok.AllArgsConstructor;
import lombok.Data;
​
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
​
​
public class UserRepository {private final DataSource dataSource;
​public UserRepository(DataSource dataSource) {this.dataSource = dataSource;}
​// 一 基础功能
​// 1 创建数据表public void initialize() {try (Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement()) {
​// 创建用户表stmt.execute("CREATE TABLE IF NOT EXISTS users (" +"id INT AUTO_INCREMENT PRIMARY KEY, " +"name VARCHAR(50) NOT NULL, " +"email VARCHAR(100) UNIQUE, " +"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
​// 创建订单表stmt.execute("CREATE TABLE IF NOT EXISTS orders (" +"id INT AUTO_INCREMENT PRIMARY KEY, " +"user_id INT NOT NULL, " +"amount DECIMAL(10,2) NOT NULL, " +"status VARCHAR(20) CHECK(status IN ('PENDING','PAID','SHIPPED','CANCELLED')), " +"order_date DATE NOT NULL, " +"FOREIGN KEY (user_id) REFERENCES users(id))");
​// 添加索引stmt.execute("CREATE INDEX IF NOT EXISTS idx_order_date ON orders(order_date)");
​} catch (SQLException e) {throw new RuntimeException("Database initialization failed", e);}}
​// 2 创建用户public int createUser(User user) {String sql = "INSERT INTO users(name, email) VALUES(?, ?)";try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
​pstmt.setString(1, user.getName());pstmt.setString(2, user.getEmail());pstmt.executeUpdate();
​try (ResultSet rs = pstmt.getGeneratedKeys()) {if (rs.next()) return rs.getInt(1);}} catch (SQLException e) {e.printStackTrace();}return -1;}
​// 3 查询用户public List<User> findUsers(int page, int size) {List<User> users = new ArrayList<>();String sql = "SELECT * FROM users ORDER BY id LIMIT ? OFFSET ?";
​try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
​pstmt.setInt(1, size);pstmt.setInt(2, (page - 1) * size);
​try (ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {users.add(mapToUser(rs));}}} catch (SQLException e) {e.printStackTrace();}return users;}
​// 4 更新用户邮箱public boolean updateUserEmail(int userId, String newEmail) {String sql = "UPDATE users SET email = ? WHERE id = ?";try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
​pstmt.setString(1, newEmail);pstmt.setInt(2, userId);return pstmt.executeUpdate() > 0;} catch (SQLException e) {e.printStackTrace();}return false;}
​// 5 删除用户public boolean deleteUser(int userId) {String sql = "DELETE FROM users WHERE id = ?";try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
​pstmt.setInt(1, userId);return pstmt.executeUpdate() > 0;} catch (SQLException e) {e.printStackTrace();}return false;}
​
​// 2 高级功能
​// 6 JOIN测试public List<UserOrderDTO> getUserOrders(LocalDate startDate) {List<UserOrderDTO> results = new ArrayList<>();String sql = "SELECT u.id AS user_id, u.name, u.email, " +"o.id AS order_id, o.amount, o.order_date " +"FROM users u " +"INNER JOIN orders o ON u.id = o.user_id " +"WHERE o.order_date >= ? " +"ORDER BY o.order_date DESC";
​try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
​pstmt.setDate(1, java.sql.Date.valueOf(startDate));
​try (ResultSet rs = pstmt.executeQuery()) {while (rs.next()) {results.add(new UserOrderDTO(rs.getInt("user_id"),rs.getString("name"),rs.getString("email"),rs.getInt("order_id"),rs.getBigDecimal("amount"),rs.getDate("order_date").toLocalDate()));}}} catch (SQLException e) {e.printStackTrace();}return results;}
​
​// 7 开启事务功能public boolean placeOrder(Order order) {Connection conn = null;try {conn = dataSource.getConnection();conn.setAutoCommit(false); // 开启事务
​// 检查用户是否存在if (!userExists(conn, order.getUserId())) {System.out.println("执行遇到错误,事务回滚.");}
​// 插入订单try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO orders(user_id, amount, status, order_date) VALUES(?,?,?,?)")) {
​pstmt.setInt(1, order.getUserId());pstmt.setBigDecimal(2, order.getAmount());pstmt.setString(3, "PENDING");pstmt.setDate(4, java.sql.Date.valueOf(order.getOrderDate()));pstmt.executeUpdate();}
​// 更新用户名称try (PreparedStatement pstmt = conn.prepareStatement("UPDATE users SET name = 'GawynKing' WHERE id = ?")) {
​pstmt.setInt(1, order.getUserId());pstmt.executeUpdate();}
​conn.commit(); // 提交事务return true;} catch (SQLException e) {if (conn != null) {try {conn.rollback(); // 回滚事务} catch (SQLException ex) {ex.printStackTrace();}}e.printStackTrace();} finally {if (conn != null) {try {conn.setAutoCommit(true); // 恢复自动提交conn.close();} catch (SQLException e) {e.printStackTrace();}}}return false;}
​
​// 8 批处理模式public int batchInsertUsers(List<User> users) {String sql = "INSERT INTO users(name, email) VALUES(?,?)";try (Connection conn = dataSource.getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) {
​for (User user : users) {pstmt.setString(1, user.getName());pstmt.setString(2, user.getEmail());pstmt.addBatch();}
​int[] results = pstmt.executeBatch();return Arrays.stream(results).sum();} catch (SQLException e) {e.printStackTrace();}return 0;}
​
​// 三 支持功能private User mapToUser(ResultSet rs) throws SQLException {return new User(rs.getInt("id"),rs.getString("name"),rs.getString("email"),rs.getTimestamp("created_at").toLocalDateTime());}
​public boolean userExists(Connection conn, Integer userId) throws SQLException {String sql = "SELECT 1 FROM users WHERE id = ?";
​try (PreparedStatement stmt = conn.prepareStatement(sql)) {stmt.setInt(1, userId);
​try (ResultSet rs = stmt.executeQuery()) {return rs.next(); // 如果查询到数据,返回 true}}}
​
​@Data@AllArgsConstructorpublic static class User {private int id;private String name;private String email;private LocalDateTime createdAt;}
​
​@Data@AllArgsConstructorpublic static class Order {private int id;private int userId;private BigDecimal amount;private String status;private LocalDate orderDate;}
​@Data@AllArgsConstructorpublic static class UserOrderDTO {private int userId;private String userName;private String email;private int orderId;private BigDecimal amount;private LocalDate orderDate;}
​
}
2.4 测试程序
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
​
public class ClientTest {
​public static void main(String[] args) {
​// 初始化数据源 和 功能类UserRepository repository = new UserRepository(H2Pool.getDataSource());
​// 初始化数据库表System.out.println("=== 初始化数据库表 ===");repository.initialize();System.out.println("数据库表初始化完成\n");
​
​// 测试创建用户System.out.println("=== 测试创建用户 ===");testCreateUser(repository);System.out.println();
​// 测试查询用户System.out.println("=== 测试查询用户 ===");testFindUsers(repository);System.out.println();
​// 测试更新用户邮箱System.out.println("=== 测试更新用户邮箱 ===");testUpdateUserEmail(repository);System.out.println("--- 更新后最新数据 ---");testFindUsers(repository);System.out.println();
​// 测试删除用户System.out.println("=== 测试删除用户 ===");testDeleteUser(repository);System.out.println("--- 删除后最新数据 ---");testFindUsers(repository);System.out.println();
​// 测试JOIN查询System.out.println("=== 测试JOIN查询 ===");testJoinQuery(repository);System.out.println();
​// 测试事务功能System.out.println("=== 测试事务功能 ===");testTransaction(repository);System.out.println("--- 启动事务后数据 ---");testJoinQuery(repository);System.out.println();
​// 测试批处理System.out.println("=== 测试批处理 ===");testBatchInsert(repository);System.out.println("--- 测试批处理最新数据 ---");testFindUsers(repository);System.out.println();}
​
​private static void testCreateUser(UserRepository repository) {UserRepository.User user1 = new UserRepository.User(0, "张三", "zhangsan@example.com", null);int id1 = repository.createUser(user1);System.out.println("创建用户1成功,ID: " + id1);
​UserRepository.User user2 = new UserRepository.User(0, "李四", "lisi@example.com", null);int id2 = repository.createUser(user2);System.out.println("创建用户2成功,ID: " + id2);
​UserRepository.User user3 = new UserRepository.User(0, "王五", "wangwu@example.com", null);int id3 = repository.createUser(user3);System.out.println("创建用户3成功,ID: " + id3);}
​private static void testFindUsers(UserRepository repository) {System.out.println("-- 第一页,每页2条 --");List<UserRepository.User> page1 = repository.findUsers(1, 2);page1.forEach(user -> System.out.println("用户: " + user.getName() + ", 邮箱: " + user.getEmail()));
​System.out.println("\n-- 第二页,每页2条 --");List<UserRepository.User> page2 = repository.findUsers(2, 2);page2.forEach(user -> System.out.println("用户: " + user.getName() + ", 邮箱: " + user.getEmail()));}
​private static void testUpdateUserEmail(UserRepository repository) {boolean updated = repository.updateUserEmail(1, "zhangsan_new@example.com");System.out.println("更新用户1邮箱结果: " + (updated ? "成功" : "失败"));
​// 查询验证List<UserRepository.User> users = repository.findUsers(1, 10);users.stream().filter(user -> user.getId() == 1).findFirst().ifPresent(user -> System.out.println("用户1新邮箱: " + user.getEmail()));}
​private static void testDeleteUser(UserRepository repository) {System.out.println("删除前用户数量: " + repository.findUsers(1, 10).size());boolean deleted = repository.deleteUser(3);System.out.println("删除用户3结果: " + (deleted ? "成功" : "失败"));System.out.println("删除后用户数量: " + repository.findUsers(1, 10).size());}
​private static void testJoinQuery(UserRepository repository) {// 先创建一些订单数据createTestOrders(repository);
​System.out.println("查询今天及以后的订单:");List<UserRepository.UserOrderDTO> orders = repository.getUserOrders(LocalDate.now());orders.forEach(order -> System.out.println("用户: " + order.getUserName() +", 订单ID: " + order.getOrderId() +", 金额: " + order.getAmount() +", 日期: " + order.getOrderDate()));}
​private static void createTestOrders(UserRepository repository) {UserRepository.Order order1 = new UserRepository.Order(0, 1, new BigDecimal("100.50"), "PENDING", LocalDate.now());UserRepository.Order order2 = new UserRepository.Order(0, 2, new BigDecimal("200.75"), "PAID", LocalDate.now().plusDays(1));UserRepository.Order order3 = new UserRepository.Order(0, 1, new BigDecimal("50.25"), "SHIPPED", LocalDate.now().minusDays(1));
​repository.placeOrder(order1);repository.placeOrder(order2);repository.placeOrder(order3);}
​private static void testTransaction(UserRepository repository) {// 测试成功的事务System.out.println("-- 测试成功的事务 --");UserRepository.Order validOrder = new UserRepository.Order(0, 1, new BigDecimal("300.00"), "PENDING", LocalDate.now());boolean success = repository.placeOrder(validOrder);System.out.println("订单创建结果: " + (success ? "成功" : "失败"));
​// 测试失败的事务 (使用无效用户ID)System.out.println("\n-- 测试失败的事务 (使用无效用户ID) --");boolean failure = false;UserRepository.Order invalidOrder = new UserRepository.Order(10, 999, new BigDecimal("400.00"), "PENDING", LocalDate.now());try{failure = repository.placeOrder(invalidOrder);}catch (Exception e){}System.out.println("订单创建结果: " + (failure ? "成功" : "失败"));}
​private static void testBatchInsert(UserRepository repository) {List<UserRepository.User> users = Arrays.asList(new UserRepository.User(0, "赵六", "zhaoliu@example.com", null),new UserRepository.User(0, "钱七", "qianqi@example.com", null),new UserRepository.User(0, "孙八", "sunba@example.com", null));
​int insertedCount = repository.batchInsertUsers(users);System.out.println("批量插入了 " + insertedCount + " 个用户");
​// 验证插入结果System.out.println("\n当前所有用户:");repository.findUsers(1, 10).forEach(user -> System.out.println("ID: " + user.getId() + ", 姓名: " + user.getName()));}
​
}

执行结果如下:

=== 初始化数据库表 ===
数据库表初始化完成
​
=== 测试创建用户 ===
创建用户1成功,ID: 1
创建用户2成功,ID: 2
创建用户3成功,ID: 3
​
=== 测试查询用户 ===
-- 第一页,每页2条 --
用户: 张三, 邮箱: zhangsan@example.com
用户: 李四, 邮箱: lisi@example.com
​
-- 第二页,每页2条 --
用户: 王五, 邮箱: wangwu@example.com
​
=== 测试更新用户邮箱 ===
更新用户1邮箱结果: 成功
用户1新邮箱: zhangsan_new@example.com
--- 更新后最新数据 ---
-- 第一页,每页2条 --
用户: 张三, 邮箱: zhangsan_new@example.com
用户: 李四, 邮箱: lisi@example.com
​
-- 第二页,每页2条 --
用户: 王五, 邮箱: wangwu@example.com
​
=== 测试删除用户 ===
删除前用户数量: 3
删除用户3结果: 成功
删除后用户数量: 2
--- 删除后最新数据 ---
-- 第一页,每页2条 --
用户: 张三, 邮箱: zhangsan_new@example.com
用户: 李四, 邮箱: lisi@example.com
​
-- 第二页,每页2条 --
​
=== 测试JOIN查询 ===
查询今天及以后的订单:
用户: GawynKing, 订单ID: 2, 金额: 200.75, 日期: 2025-06-26
用户: GawynKing, 订单ID: 1, 金额: 100.50, 日期: 2025-06-25
​
=== 测试事务功能 ===
-- 测试成功的事务 --
订单创建结果: 成功
​
-- 测试失败的事务 (使用无效用户ID) --
执行遇到错误,事务回滚.
订单创建结果: 失败
--- 启动事务后数据 ---
查询今天及以后的订单:
用户: GawynKing, 订单ID: 2, 金额: 200.75, 日期: 2025-06-26
用户: GawynKing, 订单ID: 7, 金额: 200.75, 日期: 2025-06-26
用户: GawynKing, 订单ID: 1, 金额: 100.50, 日期: 2025-06-25
用户: GawynKing, 订单ID: 4, 金额: 300.00, 日期: 2025-06-25
用户: GawynKing, 订单ID: 6, 金额: 100.50, 日期: 2025-06-25
​
=== 测试批处理 ===
批量插入了 3 个用户
​
当前所有用户:
ID: 1, 姓名: GawynKing
ID: 2, 姓名: GawynKing
ID: 4, 姓名: 赵六
ID: 5, 姓名: 钱七
ID: 6, 姓名: 孙八
--- 测试批处理最新数据 ---
-- 第一页,每页2条 --
用户: GawynKing, 邮箱: zhangsan_new@example.com
用户: GawynKing, 邮箱: lisi@example.com
​
-- 第二页,每页2条 --
用户: 赵六, 邮箱: zhaoliu@example.com
用户: 钱七, 邮箱: qianqi@example.com
​

参考资料

  1. H2官方文档:https://www.h2database.com

相关文章:

  • 外贸网站建设 深圳珠海网站建设优化
  • 做网站系统的三只松鼠网络营销方案策划书
  • 有什么网站有小学生做的题目济南网站seo优化
  • 微信公众号 网站开发 2016太原百度快速优化
  • 深圳有多少家设计公司seo营销专员
  • 网页如何发布到服务器上什么是seo?
  • DASCTF 2025上半年赛-web1 phpms Writeup
  • SpringBoot项目快速开发框架JeecgBoot——数据访问!
  • 局域网环境下浏览器安全限制的实用方法
  • 【2021 ICCV-Backbone 结构解析】Swin Transformer
  • 【python】http请求的默认超时时间设置
  • 智能体平台的商业前景与竞争格局分析:金融与企业市场的机遇与挑战
  • 深度解析Lucene IndexWriter 性能优化
  • GROUP BY、UNION和COALESCE协作
  • 【机器学习深度学习】张量基本操作
  • 无人机灯光驱动模块技术解析
  • Netty对象池ObjectPool源码解析
  • arthas助力Java程序Full GC频率大降!
  • NVIDIA A100 GPU的计算与内存层级结构
  • day042-负载均衡与web集群搭建
  • AR/VR 显示画质失真?OAS 体全息光栅案例来解决
  • Vue Devtools “Open in Editor” 配置教程(适用于 VSCode 等主流编辑器)
  • Codex+ 自建中转 API 部署教程(Windows 版)
  • 3 大语言模型预训练数据-3.2 数据处理-3.2.2 冗余去除——1.SimHash算法处理冗余信息的核心原理
  • react中使用3D折线图跟3D曲面图
  • 分布式环境下 Spring Boot 项目基于雪花算法的唯一 ID 生成方案