在 Java Web 开发中,与数据库交互是不可避免的,而 JDBC(Java Database Connectivity) 是 Java 官方提供的标准数据库连接接口,几乎所有 Java 项目中都用过它。
本文通过一个完整示例,带你从零实现 增(Insert)、删(Delete)、改(Update)、查(Select) 四大功能,并配合流程图、表结构图深入分析 JDBC 的执行原理与优化方案。
一、JDBC 基本工作流程
JDBC 是一套标准 API,任何数据库厂商只要提供 JDBC 驱动,就可以通过相同的代码访问不同数据库。它的基本执行步骤如下:
-
加载数据库驱动(Class.forName)
-
建立数据库连接(DriverManager.getConnection)
-
创建 Statement 对象(connection.createStatement)
-
执行 SQL 语句(executeQuery / executeUpdate)
-
处理结果集(ResultSet)
-
释放资源(关闭 ResultSet、Statement、Connection)
二、数据库表结构设计
我们以一个 qwe
表为例,存储学生信息。
表结构图:
+---------+-------------+----------+-------------+
| 字段名 | 数据类型 | 约束 | 描述 |
+---------+-------------+----------+-------------+
| id | INT | 主键自增 | 学生编号 |
| name | VARCHAR(50) | NOT NULL | 学生姓名 |
| age | INT | | 年龄 |
| sex | VARCHAR(5) | | 性别 |
| classid | INT | | 班级ID |
+---------+-------------+----------+-------------+
SQL 创建语句:
CREATE TABLE qwe (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT,
sex VARCHAR(5),
classid INT
);
三、完整 JDBC 代码实现 CRUD
以下代码基于 MySQL 8.0 驱动,适用于 Java SE / Java Web 项目。
package com.qcby.db;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
/**
* JDBC 操作 MySQL 实现增删改查
* 适用于 Java SE / Java Web
*/
public class Test {
public static void main(String[] args) {
// SQL 示例(注意:实际项目中应使用 PreparedStatement)
String sqlSelect = "SELECT * FROM qwe";
String sqlInsert = "INSERT INTO qwe(name, age, sex, classid) VALUES('ppp', 18, '女', 3)";
String sqlUpdate = "UPDATE qwe SET name = '修改后的名字' WHERE id = 1";
String sqlDelete = "DELETE FROM qwe WHERE id = 2";
// 调用方法
System.out.println(search(sqlSelect));
System.out.println("插入数据条数:" + add(sqlInsert));
System.out.println("修改数据条数:" + update(sqlUpdate));
System.out.println("删除数据条数:" + delete(sqlDelete));
}
/**
* 查询方法
* @param sql 查询语句
* @return JSON 格式的查询结果
*/
public static String search(String sql) {
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/javaweb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
// 3. 创建 Statement 对象
Statement statement = connection.createStatement();
// 4. 执行查询
ResultSet resultSet = statement.executeQuery(sql);
// 5. 封装结果为 JSON 格式
StringBuilder res = new StringBuilder("[");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
int age = resultSet.getInt("age");
int classid = resultSet.getInt("classid");
res.append(String.format("{\"id\":%d,\"name\":\"%s\",\"sex\":\"%s\",\"age\":%d,\"classid\":%d},",
id, name, sex, age, classid));
}
if (res.length() > 1) {
res.setLength(res.length() - 1); // 去掉最后的逗号
}
res.append("]");
// 6. 释放资源(建议在 finally 块中关闭)
resultSet.close();
statement.close();
connection.close();
return res.toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
// 添加
public static int add(String sql) {
return executeUpdate(sql);
}
// 修改
public static int update(String sql) {
return executeUpdate(sql);
}
// 删除
public static int delete(String sql) {
return executeUpdate(sql);
}
/**
* 公共执行方法(Insert、Update、Delete)
*/
private static int executeUpdate(String sql) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/javaweb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);
Statement statement = connection.createStatement();
int num = statement.executeUpdate(sql);
statement.close();
connection.close();
return num;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
改进建议:
-
资源关闭:推荐用
try-with-resources
自动关闭 Connection、Statement、ResultSet,避免忘记关闭导致连接泄露。 -
防 SQL 注入:实际开发中应使用
PreparedStatement
而不是直接拼接字符串。
💡 小 Tips:SQL 注入(SQL Injection)
定义:
SQL 注入是一种 Web 安全漏洞,攻击者通过在输入字段中插入恶意 SQL 代码,使程序在执行数据库查询时被篡改,从而绕过认证、窃取或篡改数据。
防护三步曲:
1.永远不要拼接用户输入到 SQL
// ❌ 危险
"SELECT * FROM users WHERE name='" + name + "'"
2.使用预编译(PreparedStatement)绑定参数
// ✅ 安全
String sql = "SELECT * FROM users WHERE name=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, name);
3.输入校验 + 最小权限 + 关闭错误回显
-
限制输入长度与字符范围(白名单优先)
-
数据库账号仅赋予必需权限
-
生产环境关闭详细 SQL 错误输出
四、关键知识点详解
1. 驱动加载
Class.forName("com.mysql.cj.jdbc.Driver");
-
MySQL 8.x 驱动类:
com.mysql.cj.jdbc.Driver
-
为什么手动加载? 早期 JDBC 版本需要显式加载驱动,现在有 SPI 自动加载机制,但手动加载能提高兼容性。
-
常见报错:
ClassNotFoundException
,说明mysql-connector-java
依赖未导入。
2. 数据库连接 URL
jdbc:mysql://localhost:3306/javaweb?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
-
useUnicode=true
:支持 Unicode 字符 -
characterEncoding=UTF-8
:使用 UTF-8 编码 -
serverTimezone=Asia/Shanghai
:解决时区问题
3. Statement 与 PreparedStatement区别
特性 | Statement | PreparedStatement |
---|---|---|
SQL 注入 | 有风险 | 防注入 |
执行效率 | 每次执行都编译 SQL | 预编译,提高性能 |
参数绑定 | 手动拼接 | setString /setInt |
推荐程度 | 低 | 高(实际项目都用这个) |
-
Statement:直接拼接 SQL(有 SQL 注入风险)
-
PreparedStatement:预编译 SQL,防止注入,并提高执行效率(推荐)
五、在 Servlet 中调用
在 Java Web 中,可以将这个 JDBC 工具类放到 com.utils
包中,然后在 Servlet 中调用,例如:
@WebServlet("/search")
public class SearchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 查询所有数据
String sql = "SELECT * FROM qwe";
String resultJson = Test.search(sql);
// 设置响应类型为 JSON
response.setContentType("application/json;charset=UTF-8");
// 返回数据给前端
response.getWriter().write(resultJson);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
// 如果需要支持 POST,可以在这里处理
doGet(request, response);
}
}
优化建议:
-
统一编码:在过滤器中设置
request.setCharacterEncoding("UTF-8")
。 -
返回 JSON 工具类:用
Gson
或Jackson
代替手写 JSON。 -
异常处理:Servlet 里不要直接
printStackTrace()
,建议记录到日志文件。
六、总结
本文通过完整代码演示了如何用 JDBC 操作 MySQL 实现增删改查,并配合流程图、表结构图解释了其执行过程。
在实际项目中,我们应结合 连接池 + PreparedStatement + DAO 封装,从而让代码更安全、更高效、更易维护。