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 核心特性与优势
-
多模式运行:
-
嵌入式模式:作为应用进程的一部分运行,无需独立进程
-
内存模式:数据完全存储在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@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
参考资料:
-
H2官方文档:https://www.h2database.com