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
    @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
​

参考资料

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GawynKing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值