java-JDBC

1.JDBC简介

一、关系数据库基础

  1. 数据存储需求

    • 程序数据需持久化存储,自定义格式存在读写复杂、查询低效等问题,数据库应运而生。
  2. 关系模型核心

    • 基于表结构存储数据,通过外键关联实现表间 "一对多" 关系。
    • 使用 SQL 语句查询数据,例:SELECT * FROM classes WHERE grade_id = '1'

二、NoSQL 与 SQL 对比

  • NoSQL 虽宣传速度和规模优势,但 SQL 是数据库基础,理解 SQL 是掌握 NoSQL 的前提,不可忽视。

三、数据库类别

  1. 付费商用数据库

    • 代表:Oracle、SQL Server、DB2、Sybase。
    • 特点:不开源、需付费,适合需要厂商技术支持的企业场景。
  2. 免费开源数据库

    • 推荐:

      • MySQL:普及率高、工具丰富,适合 Web 开发。
      • PostgreSQL:学术性强,功能强大但知名度较低。
      • SQLite:轻量级,适合嵌入式系统和桌面应用。

四、MySQL 安装与配置要点

  1. 版本选择

    • 下载免费的MySQL Community Server版本。
  2. 编码配置

    • Windows:安装时直接选择 UTF-8 编码。

    • Mac/Linux :修改配置文件/etc/my.cnf/etc/mysql/my.cnf,添加以下内容:

      ini 复制代码
      [client]
      default-character-set = utf8
      [mysqld]
      default-storage-engine = INNODB
      character-set-server = utf8
      collation-server = utf8_general_ci
    • 高版本优化 :若 MySQL≥5.5.3,可设置utf8mb4编码(兼容 utf8,支持 emoji 等字符)。

  3. 编码验证

    • 登录 MySQL 命令行,执行show variables like '%char%',检查结果是否包含utf8字样。

五、JDBC 核心内容

  1. 定义

    • Java Database Connectivity 的缩写,是 Java 程序访问数据库的标准接口,位于java.sql包(JDK 标准库)。
  2. 架构原理

    • Java 程序 通过 JDBC 接口调用数据库,具体实现由数据库厂商提供的 JDBC 驱动(如 MySQL 的 jar 包)完成。
    • 驱动功能:封装网络通讯细节,实现与数据库的底层交互。
  3. 核心优势

    • 接口统一:各数据库厂商遵循相同接口,Java 代码无需针对不同数据库单独开发。
    • 低依赖 :编译期仅依赖java.sql包,运行时动态加载数据库驱动 jar 包。
    • 易迁移:更换数据库时,只需替换驱动 jar 包,核心代码改动极小。

2.JDBC查询

一、JDBC 基础概念

  1. JDBC 接口与驱动

    • JDBC 是 Java 标准库java.sql中的接口规范,用于连接数据库。

    • 数据库厂商需提供 JDBC 驱动(实现接口的 jar 包),如 MySQL 驱动mysql-connector-java

    • Maven 依赖配置

      xml 复制代码
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.47</version>
          <scope>runtime</scope> <!-- 仅运行时需要,避免编译期混淆 -->
      </dependency>
  2. 数据库准备

    • 通过 SQL 脚本创建数据库、表及初始数据(以 MySQL 为例),需注意 MySQL 8.x 版本的用户创建语句差异。

二、连接数据库(Connection)

  1. URL 格式

    • 示例(MySQL):

      java 复制代码
      jdbc:mysql://localhost:3306/learnjdbc?useSSL=false&characterEncoding=utf8
      • 参数说明:hostname(主机名)、port(端口,默认 3306)、db(数据库名),useSSL关闭加密,characterEncoding指定字符编码。
  2. 获取连接

    • 使用DriverManager.getConnection(url, user, password)获取连接。

    • 资源管理最佳实践 :通过try (resource)自动释放连接,避免资源泄漏。

      java 复制代码
      try (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD)) {
          // 操作数据库
      } // 自动关闭连接

三、执行 JDBC 查询

  1. 查询步骤

    • 步骤 1 :创建StatementPreparedStatement对象。
    • 步骤 2 :执行查询语句,返回ResultSet结果集。
    • 步骤 3 :遍历ResultSet读取数据。
  2. Statement vs PreparedStatement

    • Statement :直接拼接 SQL,存在SQL 注入风险(如用户输入恶意字符串篡改查询逻辑)。

    • PreparedStatement

      • 使用?作为占位符,避免 SQL 注入。

      • 示例:

        java 复制代码
        String sql = "SELECT * FROM students WHERE gender=? AND grade=?";
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setObject(1, 1); // 设置参数(索引从1开始)
            ps.setObject(2, 3);
            try (ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    long id = rs.getLong("id"); // 按列名读取更易读
                    String name = rs.getString("name");
                }
            }
        }
      • 优势:安全、高效(可复用数据库查询缓存)。

  3. ResultSet 操作要点

    • rs.next():移动到下一行,返回boolean判断是否有数据(初始位置在无数据行 ,需先调用next())。

    • 列索引从1开始,可通过列名(如rs.getString("name"))或索引读取数据。

    • 聚合查询(如SUM(score))结果仍通过ResultSet读取:

      java 复制代码
      if (rs.next()) {
          double sum = rs.getDouble(1); // 读取第一列
      }

四、SQL 注入防护

  • 核心问题 :拼接 SQL 时,用户输入的恶意字符串可能篡改查询逻辑(如" OR 1=1 --"绕过验证)。
  • 解决方案必须使用PreparedStatement,禁止拼接 SQL 字符串。

五、数据类型映射

SQL 数据类型 Java 数据类型
BIT, BOOL boolean
INTEGER int
BIGINT long
REAL float
FLOAT, DOUBLE double
CHAR, VARCHAR String
DECIMAL BigDecimal
DATE java.sql.Date, LocalDate
TIME java.sql.Time, LocalTime
  • 注意 :仅最新 JDBC 驱动支持LocalDate/LocalTime

3.JDBC更新

(一)CRUD 概念

数据库操作主要包括增(Create)、删(Delete)、改(Update)、查(Retrieve),简称 CRUD,本文重点介绍增、改、删操作(更新操作)。

(二)插入操作(INSERT)

  • 实现方式:使用 PreparedStatement 执行 INSERT 语句,通过 executeUpdate () 方法执行,返回值为插入的记录数量(通常为 1)。

  • 示例代码要点

    • 设置参数时,占位符 "?" 的索引从 1 开始,通过 setObject () 等方法设置对应参数。
    • 示例代码:
    java 复制代码
    try (Connection conn = DriverManager.getConnection(...)) {
        try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (...) VALUES (?,?,?,?)")) {
            ps.setObject(1, 999); 
            ps.setObject(2, 1); 
            ps.setObject(3, "Bob"); 
            ps.setObject(4, "M"); 
            int n = ps.executeUpdate(); // 返回1
        }
    }
  • 注意事项:严格避免手动拼 SQL 字符串,防止安全漏洞。

(三)插入并获取自增主键

  • 适用场景:当表设置自增主键时,插入后需获取自动生成的主键值。

  • 关键步骤

    • 创建 PreparedStatement 时,指定 Statement.RETURN_GENERATED_KEYS 标志位,告知 JDBC 驱动返回自增主键。
    • 执行 executeUpdate () 后,通过 getGeneratedKeys () 获取 ResultSet 对象,从中读取主键值。
  • 示例代码要点

    java 复制代码
    try (Connection conn = ...) {
        try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (...) VALUES (?,?,?)", Statement.RETURN_GENERATED_KEYS)) {
            // 设置参数
            int n = ps.executeUpdate(); 
            try (ResultSet rs = ps.getGeneratedKeys()) {
                if (rs.next()) {
                    long id = rs.getLong(1); // 索引从1开始
                }
            }
        }
    }
  • 注意事项:若一次插入多条记录,ResultSet 可能有多行;自增列不一定是主键,可能有多个自增列。

(四)更新操作(UPDATE)

  • 实现方式:使用 PreparedStatement 执行 UPDATE 语句,通过 executeUpdate () 方法执行,返回值为实际更新的行数(可能为正数或 0)。

  • 示例代码要点

    java 复制代码
    try (Connection conn = ...) {
        try (PreparedStatement ps = conn.prepareStatement("UPDATE students SET name=? WHERE id=?")) {
            ps.setObject(1, "Bob"); 
            ps.setObject(2, 999); 
            int n = ps.executeUpdate(); // 返回更新的行数
        }
    }

(五)删除操作(DELETE)

  • 实现方式:使用 PreparedStatement 执行 DELETE 语句,通过 executeUpdate () 方法执行,返回值为删除的行数。

  • 示例代码要点

    java 复制代码
    try (Connection conn = ...) {
        try (PreparedStatement ps = conn.prepareStatement("DELETE FROM students WHERE id=?")) {
            ps.setObject(1, 999); 
            int n = ps.executeUpdate(); // 返回删除的行数
        }
    }

4. JDBC事务

一、数据库事务基础

  1. 定义

    由若干 SQL 语句构成的操作序列,确保所有操作要么全部执行成功,要么全部回滚,类似于 Java 的synchronized同步机制。

  2. ACID 特性

    • 原子性(Atomicity) :事务中的操作要么全成功,要么全失败。
    • 一致性(Consistency) :事务执行前后,数据状态保持合法。
    • 隔离性(Isolation) :多个事务并发执行时,互不干扰。
    • 持久性(Durability) :事务提交后,数据变更永久保存。

二、事务隔离级别

  1. SQL 标准定义的 4 种隔离级别

    隔离级别 脏读(Dirty Read) 不可重复读(Non Repeatable Read) 幻读(Phantom Read)
    读未提交(Read Uncommitted)
    读已提交(Read Committed) -
    可重复读(Repeatable Read) - -
    串行化(Serializable) - - -
  2. 说明

    • 脏读:事务 A 读取到事务 B 未提交的数据,若 B 回滚,A 读取的数据无效。
    • 不可重复读:事务 A 两次读取同一数据,期间事务 B 修改并提交,导致 A 两次结果不一致。
    • 幻读:事务 A 按条件查询数据,事务 B 插入 / 删除符合条件的数据并提交,导致 A 两次查询结果集不同。
    • 默认隔离级别 :MySQL 为REPEATABLE_READ,其他数据库可能不同。

三、JDBC 事务实现

  1. 关键步骤

    java 复制代码
    Connection conn = openConnection();
    try {
        conn.setAutoCommit(false); // 关闭自动提交,开启事务
        // 执行多条SQL语句(如insert、update、delete)
        conn.commit(); // 提交事务
    } catch (SQLException e) {
        conn.rollback(); // 捕获异常并回滚事务
    } finally {
        conn.setAutoCommit(true); // 恢复自动提交模式
        conn.close(); // 关闭连接
    }
  2. 核心方法

    • setAutoCommit(false):关闭自动提交,开始事务。
    • commit():提交事务,所有操作生效。
    • rollback():回滚事务,撤销所有未提交操作。
    • 注意 :默认情况下Connection处于自动提交模式(每条 SQL 单独作为事务执行),需显式关闭以开启批量事务。
  3. 设置隔离级别

    java 复制代码
    conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 设置为读已提交级别
    • 若未显式设置,使用数据库默认隔离级别。

四、示例与实践

  • 场景:转账操作需保证原子性,两条 SQL 必须在同一事务中执行:

    sql 复制代码
    UPDATE accounts SET balance = balance - 100 WHERE id = 123 AND balance >= 100;
    UPDATE accounts SET balance = balance + 100 WHERE id = 456;
    • 若第一条成功、第二条失败,事务回滚,避免资金丢失。

5.JDBC批处理

为什么需要 JDBC 批处理?

在 Java 开发中,当需要执行大量相同 SQL 语句(如批量插入、更新)时,循环单条执行会导致频繁的数据库交互,性能低下

JDBC 批处理(Batch Processing)通过将多条参数不同的 SQL 合并为一个批次执行,减少网络请求次数和数据库解析开销 ,大幅提升性能。
场景举例

  • 批量插入用户积分记录
  • 批量更新商品库存状态
  • 批量导入 Excel 数据到数据库

核心实现:三步完成批处理

1. 创建预编译语句(PreparedStatement)

使用带占位符的 SQL 语句,避免重复解析 SQL:

java 复制代码
String sql = "INSERT INTO students (name, gender, score) VALUES (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql); // conn为数据库连接
2. 循环添加参数到批次

通过addBatch()方法将不同参数组加入批次,复用同一 PreparedStatement 对象

java 复制代码
for (Student student : studentList) {
    ps.setString(1, student.getName());
    ps.setBoolean(2, student.isGender());
    ps.setInt(3, student.getScore());
    ps.addBatch(); // 添加到批次
}
3. 执行批次并处理结果

调用executeBatch()一次性执行所有语句,返回值为每个语句影响的行数数组:

java 复制代码
int[] updateCounts = ps.executeBatch(); // 执行批次
for (int count : updateCounts) {
    System.out.println("影响行数:" + count); // 输出每条语句的执行结果
}

关键优势对比:批处理 vs 单条执行

维度 单条执行 批处理执行
数据库交互次数 每次执行 1 次网络请求 整个批次仅 1 次网络请求
性能 低(尤其数据量大时) 高(性能提升 5-10 倍)
代码复杂度 简单,但循环次数多 需要维护批次逻辑

6.JDBC连接池

一、核心概念

  1. 作用

    • 避免频繁创建和销毁 JDBC 连接带来的开销,通过复用连接提高数据库操作效率。
    • 类比线程池,减少资源消耗,提升系统性能。
  2. 标准接口

    • 基于javax.sql.DataSource接口实现,该接口位于 Java 标准库,仅定义规范,需结合具体实现类使用。
  3. 常用实现

    • HikariCP(当前最流行)、C3P0、BoneCP、Druid。

二、HikariCP 使用示例

  1. 添加依赖

    • Maven 依赖:com.zaxxer:HikariCP:2.7.1(或其他版本)。
  2. 创建连接池

    java 复制代码
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); // 数据库URL
    config.setUsername("root"); // 用户名
    config.setPassword("password"); // 密码
    // 配置参数
    config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
    config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒
    config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10
    DataSource ds = new HikariDataSource(config); // 创建连接池实例
  3. 获取与释放连接

    java 复制代码
    try (Connection conn = ds.getConnection()) { // 从连接池获取连接
        // 执行SQL操作
    } // 自动释放连接回连接池(非真正关闭)

三、关键要点

  1. 全局变量存储

    • DataSource创建成本高,需作为全局变量贯穿应用生命周期,示例:

      • 使用静态变量:static Map<String, Object> global = new HashMap<>(); global.put("dataSource", ds);
      • Web 场景可绑定到ServletContext
  2. 连接复用机制

    • 首次获取连接时创建,调用conn.close()时释放回池,标记为 "空闲" 而非销毁。
  3. 配置参数

    • 核心参数:最小 / 最大连接数、空闲超时时间、连接超时时间等,需根据负载合理配置。
    • 多数连接池支持实时状态监控。
相关推荐
皮皮林5512 小时前
SpringBoot 加载外部 Jar,实现功能按需扩展!
java·spring boot
郑道2 小时前
Docker 在 macOS 下的安装与 Gitea 部署经验总结
后端
3Katrina2 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
rocksun2 小时前
认识Embabel:一个使用Java构建AI Agent的框架
java·人工智能
汪子熙2 小时前
HSQLDB 数据库锁获取失败深度解析
数据库·后端
高松燈2 小时前
若伊项目学习 后端分页源码分析
后端·架构
没逻辑3 小时前
主流消息队列模型与选型对比(RabbitMQ / Kafka / RocketMQ)
后端·消息队列
倚栏听风雨3 小时前
SwingUtilities.invokeLater 详解
后端
Java中文社群3 小时前
AI实战:一键生成数字人视频!
java·人工智能·后端
王中阳Go4 小时前
从超市收银到航空调度:贪心算法如何破解生活中的最优决策谜题?
java·后端·算法