目录
第一部分:H2数据库系统介绍
1.1 H2数据库概述
H2数据库是一个开源的关系型数据库管理系统(RDBMS),完全由Java语言编写。它由Thomas Mueller创建,首个版本发布于2005年,旨在成为高性能、轻量级的数据库解决方案。与传统数据库相比,H2最显著的特点是零配置部署和嵌入式运行能力,这使得它成为开发、测试和小型应用部署的理想选择。
1.2 核心特性与优势
-
多模式运行:
-
嵌入式模式:作为应用进程的一部分运行,无需独立进程
-
内存模式:数据完全存储在RAM中,速度极快
-
服务端模式:独立TCP服务,支持多客户端连接
-
混合模式:同时支持嵌入式和远程连接
-
-
性能表现:
-
内存模式下操作速度可比传统数据库快10-100倍
-
支持高效的索引算法(B-tree、树状索引)
-
批量操作优化,支持预编译语句缓存
-
-
兼容性优势:
-
支持标准SQL
-
提供兼容模式(Oracle、SQL Server、MySQL等方言)
-
JDBC API完全兼容(JDBC规范)
-
-
安全特性:
-
强加密支持(AES-128/AES-256)
-
基于角色的访问控制
-
数据库文件加密
-
1.3 典型应用场景
-
单元测试与集成测试:内存数据库特性完美支持自动化测试
-
嵌入式应用:桌面软件、移动应用、IoT设备等资源受限环境
-
快速原型开发:无需复杂环境配置即可启动开发
-
缓存层实现:作为应用和主数据库之间的缓冲层
-
临时数据分析:快速处理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 @AllArgsConstructor public static class User { private int id; private String name; private String email; private LocalDateTime createdAt; } @Data @AllArgsConstructor public static class Order { private int id; private int userId; private BigDecimal amount; private String status; private LocalDate orderDate; } @Data @AllArgsConstructor public 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
参考资料: