从入门到进阶:彻底掌握JDBC编程
一、JDBC概述
1. JDBC是什么?
JDBC(Java DataBase Connectivity)是 Java 提供的一套用于操作关系型数据库的 API。它为 Java 应用程序与各种主流数据库(如 MySQL、Oracle、PostgreSQL、SQL Server 等)之间的通信提供了标准接口。
简单来说,JDBC 就是一座桥梁,它让 Java 程序可以像操作普通对象一样去执行数据库中的增删改查操作。开发者只需要通过 JDBC 提供的接口发送 SQL 语句,就可以完成对数据库的访问,而无需关心底层数据库的具体实现细节。
这套 API 主要包含了以下几类功能:
- 连接数据库:通过驱动程序建立 Java 应用与数据库之间的连接;
- 执行 SQL 语句:包括增删改查等各类 SQL 操作;
- 处理结果集:通过
ResultSet等接口处理查询返回的数据; - 事务管理:支持手动控制事务提交与回滚;
- 异常处理:通过
SQLException对各种数据库操作异常进行处理。
JDBC 是 Java EE 中最基础的持久化技术,许多高级的 ORM 框架(如 Hibernate、MyBatis)也都是建立在 JDBC 的基础之上。
2. JDBC的本质和工作机制
JDBC 的本质,其实是一套由 Sun 公司(现为 Oracle)定义的接口规范。这套规范标准化了 Java 程序如何与各种关系型数据库进行交互,其核心思想就是“面向接口编程”。
具体来说,JDBC 定义了一系列通用的接口,例如:
Connection:数据库连接对象Statement/PreparedStatement:执行 SQL 的对象ResultSet:处理查询结果的对象
这些接口本身并不包含具体实现,它们只是一个规范的“承诺”。真正的实现是由各个数据库厂商来完成的。每个厂商会根据 JDBC 的规范,编写自己的一套驱动程序,并将其打包成一个 JAR 包(Java Archive),我们通常称之为 “数据库驱动”。
例如:
- MySQL 提供了
mysql-connector-java驱动; - Oracle 提供了
ojdbc驱动; - PostgreSQL 提供了
postgresql驱动。
当我们在 Java 程序中调用 JDBC 的接口时,真正被执行的,其实是这些驱动 JAR 包中对应的实现类。我们写的是面向 JDBC 接口的代码,运行时由驱动程序完成实际的数据库操作。
这也正是 JDBC 强大之处的体现:应用程序只需依赖 JDBC 接口,不需要依赖具体的数据库实现。要切换数据库,只需更换驱动包和少量配置,无需改动核心代码,实现了良好的解耦和跨数据库的可移植性。
二、JDBC基础操作实践
1. 使用 JDBC 执行简单的 UPDATE 语句
以下是一个使用 JDBC 向数据库中执行更新操作的完整示例。我们以修改 user 表中某条记录的用户名为例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JdbcUpdateExample {
public static void main(String[] args) throws Exception {
// 注册数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver"); // 加载 MySQL 驱动类
// 获取数据库连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testdb", // 数据库地址和名称
"root", // 用户名
"password" // 密码
);
// 创建执行 SQL 的 Statement 对象
Statement stmt = conn.createStatement(); // 创建 Statement 对象用于执行 SQL
// 编写 SQL 语句
String sql = "UPDATE user SET username='newName' WHERE id=1"; // 要执行的更新语句
// 执行 SQL 语句
int rows = stmt.executeUpdate(sql); // 执行更新操作,返回受影响的行数
// 输出结果
System.out.println("更新成功,受影响的行数:" + rows); // 打印受影响的行数
// 释放资源
stmt.close(); // 关闭 Statement
conn.close(); // 关闭 Connection
}
}
补充说明:
- 在实际开发中,数据库的地址、用户名和密码建议不要硬编码到代码中,通常会使用配置文件进行管理。
executeUpdate方法适用于INSERT、UPDATE和DELETE操作,返回一个整数表示影响的行数。- 切记操作完成后要 关闭资源,避免连接泄漏,这在高并发场景下尤其重要。
2. 使用 JDBC 执行 SELECT 查询,并封装到 User 对象中
以下示例展示如何查询数据库中 user 表的某条记录,并将其封装为一个 Java 对象。首先,我们定义一个 User 实体类:
// User.java
public class User {
private int id;
private String username;
private String password;
// 构造方法、getter 和 setter 略(可使用 IDE 自动生成)
public String toString() {
return "User{id=" + id + ", username='" + username + "', password='" + password + "'}";
}
}
接着,我们编写一个简单的 JDBC 查询程序:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcSelectExample {
public static void main(String[] args) throws Exception {
// 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 建立连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testdb",
"root",
"password"
);
// 创建 Statement
Statement stmt = conn.createStatement();
// 编写 SQL 查询语句
String sql = "SELECT id, username, password FROM user WHERE id = 1";
// 执行查询
ResultSet rs = stmt.executeQuery(sql); // 返回结果集对象
// 创建用户对象
User user = null;
// 处理结果集
if (rs.next()) { // 如果查询结果有数据
user = new User(); // 创建 User 实例
user.setId(rs.getInt("id")); // 设置 id
user.setUsername(rs.getString("username")); // 设置用户名
user.setPassword(rs.getString("password")); // 设置密码
}
// 输出查询结果
System.out.println("查询结果:" + user);
// 释放资源
rs.close();
stmt.close();
conn.close();
}
}
补充说明:
ResultSet的next()方法用于向下移动一行,初始指向的是第一行之前;- 使用
getXXX方法获取指定列的数据,参数可以是列名或列索引; - 封装为 Java 对象是面向对象开发中常见的模式,便于后续处理、逻辑判断、传输等操作。
三、JDBC进阶应用
1. 深入理解结果集对象 ResultSet
在使用 JDBC 执行查询操作时,我们会经常接触到 ResultSet 接口。它代表 SQL 查询语句的结果集,也就是我们从数据库中读取的数据。
查询语句通常通过如下方式获取 ResultSet:
ResultSet rs = statement.executeQuery("SELECT * FROM user");
这行代码的含义是:使用 Statement 对象执行一条 SELECT 语句,并将查询结果返回到 ResultSet 对象中。
(1)next() 方法详解
ResultSet 中的记录默认从第一行之前开始,需要通过调用 next() 方法将游标向前推进到第一条记录:
while (rs.next()) {
// 读取当前行数据
}
rs.next()返回true表示当前行有效,继续循环;- 返回
false表示已经到达结果集末尾,结束循环; - 每次调用
next(),游标都会向下移动一行。
这一机制非常适合通过 while 循环逐行处理查询结果。
(2)getXXX() 方法使用
在移动到某一行后,我们可以通过 getXXX() 方法读取该行的每一列数据:
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
常见的 getXXX() 方法有:
| 方法名 | 返回类型 | 示例字段类型 |
|---|---|---|
getInt(String) | int | 整型字段(如 id) |
getString(String) | String | 字符串字段(如 name) |
getDouble(String) | double | 浮点型字段(如价格) |
getDate(String) | Date | 日期字段 |
参数既可以是 列名(推荐),也可以是 列索引(从 1 开始),如:
rs.getString(2); // 获取第二列的字符串内容
示例代码片段
while (rs.next()) {
System.out.println("用户ID:" + rs.getInt("id"));
System.out.println("用户名:" + rs.getString("username"));
System.out.println("密码:" + rs.getString("password"));
}
2. 静态 SQL 与预编译 SQL(PreparedStatement)的对比
在 JDBC 中,我们可以通过 Statement 或 PreparedStatement 来执行 SQL 语句。它们在使用方式和底层机制上有本质差异,以下我们通过示例代码对比、差异分析和优缺点总结,来全面理解这两种方式。
(1)示例代码对比
使用 Statement 的静态 SQL:
String username = "admin";
String password = "123456";
String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
使用 PreparedStatement 的预编译 SQL:
String username = "admin";
String password = "123456";
String sql = "SELECT * FROM user WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
(2)差异解析
| 对比维度 | Statement(静态 SQL) | PreparedStatement(预编译 SQL) |
|---|---|---|
| 编写方式 | 直接拼接 SQL 语句 | 使用占位符 ? 进行参数绑定 |
| 安全性 | 易受 SQL 注入攻击影响 | 安全性高,自动转义输入防注入攻击 |
| 性能优化 | 每次执行都重新编译 | SQL 语句预编译后可复用,提升性能 |
| 可读性与维护性 | 拼接复杂,难以阅读和维护 | 逻辑清晰,结构更清晰 |
| 数据类型支持 | 参数需手动拼接,类型容易出错 | 提供 setXXX() 方法,类型明确 |
(3)优缺点分析
Statement(静态 SQL)
- 优点:
- 简单直观,适合快速测试和临时性查询。
- 缺点:
- 安全性差,容易被 SQL 注入攻击;
- 每次执行都需要解析和编译 SQL,效率低;
- 拼接字符串容易出错,尤其是处理引号和空值。
PreparedStatement(预编译 SQL)
- 优点:
- 自动防止 SQL 注入;
- 可读性好,代码更清晰;
- SQL 语句编译一次可多次执行,性能更好;
- 支持更复杂的数据类型(如二进制、大文本等)。
- 缺点:
- 初学者需要学习新的 API;
- 某些极简 SQL 场景略显“冗长”。
总结来说,在实际开发中推荐优先使用 PreparedStatement,它不仅更安全,也更适合维护和扩展。Statement 更多用于临时查询或一些特殊场景下的动态 SQL 构造(但也建议结合工具类封装处理)。
3. SQL 注入及其防范
SQL 注入是一种常见的安全漏洞,攻击者通过在输入参数中注入恶意 SQL 片段,从而执行非法操作,如绕过登录验证、篡改数据、甚至删除数据库表。
例如,以下静态 SQL 写法容易被注入:
String sql = "SELECT * FROM user WHERE username = '" + input + "'";
如果用户输入 admin' OR '1'='1,则整个语句变为:
SELECT * FROM user WHERE username = 'admin' OR '1'='1'
这将绕过验证,返回所有用户信息。
如何防止?
- 使用
PreparedStatement,参数通过占位符传递,自动处理转义; - 永远不要信任用户输入,必要时结合白名单校验;
- 配置数据库账号权限,限制高危操作。
一句话总结:永远用 PreparedStatement 替代拼接 SQL。
四、知识总结与思维导图
1.内容思维导图

2. 总结
本文从 JDBC 的基本概念讲起,带领你掌握了连接数据库、执行增删改查操作、处理结果集等基础能力。在进阶部分,我们深入探讨了 ResultSet 的使用细节、静态 SQL 与预编译 SQL 的对比,以及 SQL 注入的危害与防御手段。
核心记忆要点:
- JDBC 是 Java 官方提供的数据库访问规范,驱动实现由厂商提供;
- 数据库操作流程包括:加载驱动 → 获取连接 → 执行 SQL → 处理结果 → 释放资源;
- 推荐使用
PreparedStatement替代Statement,更安全、更高效; - 时刻注意输入校验,避免 SQL 注入风险。
1567

被折叠的 条评论
为什么被折叠?



