① JDBC 是什么
JDBC = Java Database Connectivity,Java 官方提供的「访问数据库的统一接口」。
| 要点 | 说明 |
|---|---|
| 本质 | Java 提供一组接口(java.sql.*),任何数据库都能连 |
| 好处 | 切换数据库(MySQL→Oracle)时,业务代码基本不用改 |
- 只负责「连接 + 执行 SQL + 拿结果」,SQL 本身还得你自己写
- 搭配「数据库驱动」(driver),MySQL 驱动叫
mysql-connector-j
理解:JDBC 就像「电源插头标准」,各家数据库是"插座",驱动就是接口转换头——只要都遵守 JDBC 标准就能插上。
② 环境准备 / 驱动引入
要连 MySQL,得先引入对应驱动 jar。
方式一:手动放 jar
把 mysql-connector-j-x.x.x.jar 拷到项目,加进 classpath。
方式二:Maven 依赖(推荐)
<!-- pom.xml 里加 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
注意:MySQL 8 驱动类名是
com.mysql.cj.jdbc.Driver(旧版 5.x 是 com.mysql.jdbc.Driver),URL 写法也不一样。③ 连接五步核心流程
无论连什么库,都是这套固定套路。
1. 加载驱动(可省略)
Class.forName("com.mysql.cj.jdbc.Driver");
2. 获取连接 Connection
try(Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/数据库名",
"user", "password")){
3. 创建语句
PreparedStatement ps = conn.prepareStatement(sql);
4. 执行
ResultSet rs = ps.executeQuery(); // 查询
5. 处理结果 / 关闭
... // 遍历 rs
} // try-with-resources 自动关闭
要点:5 个对象要按顺序关:
ResultSet → Statement → Connection。用 try-with-resources(把对象写进 try 括号里)可以自动关闭,不会忘。④ 执行 SQL 的三种方法
| 方法 | 适用 | 返回 |
|---|---|---|
executeQuery() | 查询 (SELECT) | ResultSet 结果集 |
executeUpdate() | 增/删/改 (INSERT/UPDATE/DELETE/DDL) | int 受影响行数 |
execute() | 不确定是查还是改 | boolean |
ps.executeUpdate(); // 插入1行 返回1
rs = ps.executeQuery(); // 查询 → ResultSet
⑤ PreparedStatement vs Statement(必考点)
开发中永远用 PreparedStatement,不用 Statement。
| Statement | PreparedStatement | |
|---|---|---|
| 写法 | SQL 拼字符串 | 用 ? 占位,再 set 值 |
| SQL 注入 | 危险 ❌ | 安全 ✅(值被转义) |
| 性能 | 每次编译 | 可预编译、反复复用 |
| 可读性 | 易出错 | 清晰 |
String sql = "INSERT INTO user(name,age) VALUES(?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, "Tom"); // 第1个 ?
ps.setInt(2, 20); // 第2个 ?(从1开始)
int rows = ps.executeUpdate();
为什么安全:
' OR '1'='1 这类恶意输入,用 ? 占位会被当成普通数据而非 SQL 代码,从而防注入。⑥ ResultSet 结果集
查询结果像一张"表格",用游标逐行读取。
resultSet.next(); // 移到下一行,有数据返回 true,没有返回 false
resultSet.getString("name"); // 按列名取字符串
resultSet.getInt("age"); // 按列名取整数(也可用列下标从1开始)
while(rs.next()){
System.out.println(rs.getString("name") + " " + rs.getInt("age"));
}
⑦ 事务 Transaction
一组操作要么全成功、要么全回滚,保证数据一致(如银行转账)。
conn.setAutoCommit(false); // 关掉自动提交,开启事务
try{
ps1.executeUpdate(); // 扣款
ps2.executeUpdate(); // 加款
conn.commit(); // 都成功才提交
}catch(Exception e){
conn.rollback(); // 出错全部回滚
}
finally{
conn.setAutoCommit(true); // 恢复
}
四大特性 ACID:原子性(全成功或全失败)、一致性、隔离性(并发互不干扰)、持久性(提交后永久保存)。
⑧ 连接池 Connection Pool
每次连接都「建→关」很浪费(握手慢)。连接池提前建好一批复用。企业开发标配。
| 主流连接池 | 说明 |
|---|---|
HikariCP 👑 | 最快的,Spring Boot 默认用它 |
Druid | 阿里开源的,自带监控,国内常用 |
C3P0/DBCP | 老牌,现在少用 |
// 用连接池后,拿连接方式变了:
DataSource ds = ...; // 从池里拿
Connection conn = ds.getConnection(); // 拿到的不是新建,而是池里借的
// close 不是真的关,而是"还回去"给池复用
⑨ 常见坑
- 忘记关闭资源 → 连接泄漏。用
try-with-resources解决 - URL 写错/没指定时区 → MySQL8 要加
?useSSL=false&serverTimezone=Asia/Shanghai - 驱动类不存在 ClassNotFoundException → 检查 jar 是否引入
- SQL 注入 → 永远用
PreparedStatement - 列/表名不匹配 → SQL 用反引号
`或检查大小写 - 中文乱码 → URL 加
characterEncoding=utf8 - Connection closed → 可能是连接超时或没关对
⑩ 完整 Demo
一整套「连接 → 查询所有用户 → 打印」的可复制代码。
import java.sql.*;
public class JdbcDemo {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/test" +
"?useSSL=false&serverTimezone=Asia/Shanghai";
String user = "root", pass = "123456";
// try-with-resources:自动关闭 conn/ps/rs
try(Connection conn = DriverManager.getConnection(url,user,pass);
PreparedStatement ps = conn.prepareStatement(
"SELECT id,name,age FROM user");
ResultSet rs = ps.executeQuery()) {
while(rs.next()){
System.out.println(rs.getInt("id") + " "
+ rs.getString("name") + " "
+ rs.getInt("age"));
}
} catch(SQLException e){
e.printStackTrace();
}
}
}
总结:JDBC 就 5 步——加载驱动、连接、写SQL、执行、处理结果。真正开发通常用 MyBatis / JPA 等框架封装,但底仍然跑的是 JDBC,理解它才能懂上层框架。