Article details

JDBC速成

nano71.com
2025/5/20 13:58:34
0 comments
38 views

一、JDBC 简介

什么是 JDBC?

  • JDBC(Java Database Connectivity)是 Java 官方提供的一套 API,集成在 java.sql 和 javax.sql 包中

  • JDBC 允许 Java 程序连接和操作各种关系型数据库,如 MySQL、Oracle、PostgreSQL、SQL Server 等

JDBC 是基于驱动的架构:

Java 应用程序
       ↓
JDBC API(Connection、Statement 等)
       ↓
JDBC Driver(由数据库厂商提供)
       ↓
数据库(MySQL、Oracle、PostgreSQL等)

二、JDBC 核心组件

类/接口 说明
DriverManager 管理 JDBC 驱动和数据库连接
Connection 表示数据库连接
Statement 执行静态 SQL 语句
PreparedStatement 执行预编译 SQL,防止 SQL 注入
CallableStatement 执行数据库存储过程
ResultSet 表示查询结果集
ResultSetMetaData 获取结果集列信息
DatabaseMetaData 获取数据库结构信息
SQLException 数据库操作异常

三、JDBC 使用六步法(详解)

第一步:加载数据库驱动(可选)

Class.forName("com.mysql.cj.jdbc.Driver");
  • MySQL 8.x:com.mysql.cj.jdbc.Driver
  • Oracle:oracle.jdbc.driver.OracleDriver

注意:JDBC 4.0+ 不强制要求手动加载驱动,JDBC 4.0 引入了 SPI(Service Provider Interface) 机制,DriverManager 会自动加载并注册


第二步:获取数据库连接

String url = "jdbc:mysql://localhost:3306/database?serverTimezone=Asia/Shanghai";
Connection conn = DriverManager.getConnection(url, "username", "password");

URL 格式:

jdbc:mysql://主机名:端口/数据库名?参数=值&参数2=值2

第三步:创建执行对象

Statement(容易 SQL 注入)

// 不安全的写法(字符串拼接会有 SQL 注入风险)
String name = "flying.";
String sql = "SELECT * FROM user WHERE name = '" + name + "'";
Statement stmt = conn.createStatement();
stmt.executeQuery(sql)

PreparedStatement(推荐使用)

问号 ? 是参数占位符,用于给 setInt 或 setString 指定参数

String sql = "SELECT * FROM user WHERE id = ? and name = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, 1001);
ps.setString(2, "flying.");

第四步:执行 SQL 语句

查询(返回 ResultSet)

ResultSet rs = ps.executeQuery();

如果 SQL 语句是增删改(只返回影响的行数)

int count = ps.executeUpdate();

第五步:处理结果集

while (rs.next()) {
    int id = rs.getInt("id"); // 获取id字段,int类型
    String name = rs.getString("name"); // 获取name字段,string类型(varchar、char)
}

第六步:关闭资源(从小到大)

rs.close();
ps.close();
conn.close();

也可以用 Java 7+ 的 try-with-resources:

try (Connection conn = ...; PreparedStatement ps = ...) {
    // todo
}

四、PreparedStatement 与 Statement 对比

特性 Statement PreparedStatement
SQL 拼接 手动拼接 使用参数占位符 ?
安全性 易被 SQL 注入攻击 安全
性能 SQL 每次重新编译 支持预编译,效率更高
可读性 差 好

五、常用 JDBC 方法解释

Connection 接口常用方法

方法 功能
createStatement() 创建 Statement
prepareStatement(String sql) 创建预编译语句
setAutoCommit(boolean) 设置是否自动提交事务
commit() 提交事务
rollback() 回滚事务
close() 关闭连接

Statement 常用方法

方法 功能
executeQuery(String sql) 执行查询语句,返回 ResultSet
executeUpdate(String sql) 执行更新语句,返回影响行数
execute(String sql) 可执行任意语句
addBatch() 添加批处理
executeBatch() 执行批处理
clearBatch() 清除批处理

PreparedStatement 常用方法(其他方法都继承自 Statement)

注意:

  • 继承的 executeQuery 、 executeUpdate 和 execute 这三个方法是不用参数的

  • 且 setInt 、setString 和 setDate 等 setXxx 方法的第一个参数是指定 SQL 语句中的哪一个 ? 占位符(从 1 开始),第二个参数是给 ? 占位符赋的值

方法 功能
executeQuery() 执行查询语句,返回 ResultSet
executeUpdate() 执行更新语句,返回影响行数
execute() 可执行任意语句
setInt(int, int) 设置整型参数
setString(int, String) 设置字符串参数
setFloat(int, double) 设置浮点型参数
setDouble(int, double) 设置双精度浮点型参数
setBoolean(int, boolean) 设置布尔型参数
setDate(int, Date) 设置日期参数

CallableStatement 常用方法(继承 PreparedStatement 的方法)

CallableStatement 不常用,不记也行

方法 功能
prepareCall(String sql) 创建 CallableStatement
registerOutParameter(int index, SQLType) 注册 OUT 参数
getXxx(int index) 获取 OUT 参数结果

ResultSet 常用方法

注意:

  • ResultSet 对象是 executeQuery() 方法的返回值

  • getXxx 和 上面 PreparedStatement 的各种 set 方法是对应的,代入即可,如既然有 setInt 那就肯定有 setInt

方法 功能
next() 移动到下一行数据,有下一行数据则返回 True
privious()() 移动到上一行数据,作用同上
getString("列名") 获取字符串数据
getInt("列名") 获取整数数据
wasNull() 判断上一个读取值是否为 null
close() 关闭结果集

六、事务管理

JDBC 默认自动提交。可以通过以下代码实现手动提交:

示例:

conn.setAutoCommit(false);
try {
    // SQL 操作
    conn.commit();
} catch (Exception e) {
    conn.rollback(); // 回滚是指本次的批量 SQL 操作都撤消掉
}

设置隔离级别

conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

隔离级别说明:

隔离级别 常见问题 描述
READ_UNCOMMITTED 脏读 最低级别
READ_COMMITTED 不可重复读 Oracle 默认
REPEATABLE_READ 幻读 MySQL 默认
SERIALIZABLE 性能最低,最安全 完全串行执行

七、批处理执行(Batch Processing)

优点:

  • 提高执行效率
  • 减少网络通信次数

使用场景:

  • 大量重复执行相同结构的 SQL(如插入、更新)

示例:

String sql = "INSERT INTO users(name, age) VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);

for (int i = 0; i < 1000; i++) {
    ps.setString(1, "user" + i);
    ps.setInt(2, 20 + i % 10);
    ps.addBatch(); // 添加到批处理队列

    if (i % 100 == 0) {
        ps.executeBatch(); // 每 100 条执行一次
        ps.clearBatch();   // 清空批处理队列
    }
}

ps.executeBatch(); // 执行队列剩余语句,如:不足 100 条的情况出现
ps.close();

八、分页查询(以 MySQL 为例)

用于查询数据库大表时分批显示数据。

语法:

SELECT * FROM 表名 LIMIT ?, ?
  • 第一个 ?:起始位置(从 0 开始)
  • 第二个 ?:每页数量

示例:

String sql = "SELECT * FROM user LIMIT ?, ?";
PreparedStatement ps = conn.prepareStatement(sql);
int page = 2;
int pageSize = 10;
ps.setInt(1, (page - 1) * pageSize);
ps.setInt(2, pageSize);
ResultSet rs = ps.executeQuery();

九、ResultSetMetaData / DatabaseMetaData 使用

这两不常用,不记也行

ResultSetMetaData:用于获取结果集的列信息

ResultSetMetaData meta = rs.getMetaData();
int columnCount = meta.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
    System.out.println(meta.getColumnName(i));
}

DatabaseMetaData:获取数据库结构信息

DatabaseMetaData dbMeta = conn.getMetaData();
ResultSet rs = dbMeta.getTables(null, null, "%", new String[]{"TABLE"});

十、常见异常与解决方案

报错信息 原因 解决办法
ClassNotFoundException 未加载驱动 添加驱动类/依赖
SQLException: Access denied 数据库用户名或密码错误 检查登录信息
Communications link failure 数据库未启动或 IP 错误 启动数据库/检查 URL
No suitable driver URL 或驱动不兼容 检查驱动版本和 JDBC URL

总结重点

  • JDBC 是数据库编程的基础
  • PreparedStatement 安全又高效
  • 事务管理需掌握:手动控制、异常回滚
  • 批处理与分页能显著提升性能

完整 JDBC 查询示例代码

面向过程风格

import java.sql.*;

public class Example {

    public static void main(String[] args) {
        // 1. JDBC连接参数
        String url = "jdbc:mysql://localhost:3306/database?serverTimezone=Asia/Shanghai";
        String user = "root";          // 数据库用户名
        String password = "123456";    // 数据库密码

        // 2. JDBC资源
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            // 3. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 4. 建立数据库连接
            conn = DriverManager.getConnection(url, user, password);

            // 5. 编写 SQL 查询语句
            String sql = "SELECT id, name, age FROM user WHERE age > ?";

            // 6. 创建 PreparedStatement
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 18); // 查询年龄大于 18 的用户

            // 7. 执行查询,返回结果集
            rs = ps.executeQuery();

            // 8. 处理结果集
            System.out.println("查询结果:");
            while (rs.next()) {
                int id = rs.getInt("id");              // 或 rs.getInt(1)
                String name = rs.getString("name");    // 或 rs.getString(2)
                int age = rs.getInt("age");            // 或 rs.getInt(3)

                System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n", id, name, age);
            }

        } catch (ClassNotFoundException e) {
            System.out.println("数据库驱动未找到!");
        } catch (SQLException e) {
            System.out.println("数据库操作异常!");
        } finally {
            // 9. 释放资源(从小到大)
            try { if (rs != null) rs.close(); } catch (SQLException ignored) {}
            try { if (ps != null) ps.close(); } catch (SQLException ignored) {}
            try { if (conn != null) conn.close(); } catch (SQLException ignored) {}
        }
    }
}

面向对象风格

import java.sql.*;

public class Example {

        public static void main(String[] args) {
        // JDBC 连接参数
        String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC";
        String user = "root";
        String password = "123456";

        // 查询语句
        String sql = "SELECT id, name, age FROM user WHERE age > ?";

        // try-with-resources 会自动关闭资源,无需手动 close()
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            try (
                    // 2. 建立连接
                    Connection conn = DriverManager.getConnection(url, user, password);

                    // 3. 创建 PreparedStatement
                    PreparedStatement ps = conn.prepareStatement(sql);
            ) {
                // 4. 设置参数
                ps.setInt(1, 18);

                // 5. 执行查询
                try (ResultSet rs = ps.executeQuery()) {
                    System.out.println("查询结果:");
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String name = rs.getString("name");
                        int age = rs.getInt("age");

                        System.out.printf("ID: %d, 姓名: %s, 年龄: %d\n", id, name, age);
                    }
                }

            }
        } catch (ClassNotFoundException e) {
            System.out.println("数据库驱动未找到!");
        } catch (SQLException e) {
            System.out.println("数据库操作异常!");
        }
    }
}

nano71.com
2025/5/20 13:58:34
0 comments
38 views
Welcome to myBlog-site
Recent
Category
Guestbook
2026/3/10 16:11:31

解决 Dify 数据库插件连接 SQL Server 2008/2012 报错 DB-Lib error message 20002, severity 9

(pymssql.exceptions.OperationalError) (20002, b'DB-Lib error message 20002, severity 9:\nAdaptive Server connection failed (192.168.0.223)\nDB-Lib error message 20002, severity 9:\nAdaptive Server connection failed (192.168.0.223)\n') (Background on this error at: https://sqlalche.me/e/20/e3q8)

view
coverImage
2025/8/21 15:53:02

代码还原 Pinia 官网上的那个可以互动的小菠萝

眨眼 眼球跟随鼠标 一定时间鼠标不移动眼球归位

view
coverImage
2025/5/20 13:58:34

JDBC速成

JDBC(Java Database Connectivity)是 Java 官方提供的一套 API,集成在 java.sql 和 javax.sql 包中 JDBC 允许 Java 程序连接和操作各种关系型数据库,如 MySQL、Oracle、PostgreSQL、SQL Server 等

view
coverImage
2024/11/25 13:34:21

Node.js实现一个数据缓存服务器

需求场景 公司的数据服务器每次更新部署时,会有一个服务不可用的时间以及服务重新启动的时间,但如果恰巧这段时间有用户进行访问。。。当然,这只适应于非即时性的数据

view
coverImage
2024/7/18 19:30:34

Go的高并发实现:多协程和任务队列设计

前言 随着大学生活的结束,我入职了浙江的一家属于电气制造业的公司,岗位是数据分析,我算是暂时脱离了互联网这一行业,以后可能就很少接触Web开发了;把曾经对Web电子邮件系统中的性能优化的一部分抽出来记录下,也算在表达我对Web后端开发的一种敬意了

view
coverImage
2024/4/25 18:07:59

针对SPA网站的SEO方式探索-2

前言 针对SPA网站,本文将使用Nginx+Express+Puppeteer实现一个高效的SEO工具,大致的实现思路为:Nginx判断UA再进行代理转发,Express负责监听转发的请求,Puppeteer接到Express命令后启动一个无用户界面的Chrome对目标页面进行访问,等待目标SPA页面加载完成后,再返回完整的HTML给爬虫

view
coverImage
loading-GIF