Java JDBC 超详细全知识点整理
本文是Java JDBC 最全、最细、无死角的知识点总结,从基础概念、核心API、执行流程,到高级特性、性能优化、分布式事务、常见问题全覆盖,适合零基础入门、面试复习、项目实战。
一、JDBC 基础概念
1. 什么是 JDBC
JDBC(Java Database Connectivity)是Java 官方定义的一套用于执行 SQL 语句的 API(接口规范),它让 Java 程序可以统一方式连接、操作各种关系型数据库(MySQL、Oracle、SQLServer、PostgreSQL 等)。
- 本质:接口 + 实现类
- 作用:屏蔽不同数据库的底层差异,实现「一套代码,多数据库运行」
2. JDBC 核心架构
- JDBC API:Java 官方提供,开发者直接使用(
java.sql、javax.sql包) - JDBC Driver:数据库厂商实现的驱动类(实现 JDBC 接口),负责和数据库通信
- Java 应用程序:调用 JDBC API,驱动自动完成底层通信
3. JDBC 驱动类型(4 种)
| 类型 | 名称 | 特点 | 适用场景 |
|---|---|---|---|
| 1 | JDBC-ODBC 桥驱动 | 性能差、依赖本地 ODBC | 已废弃 |
| 2 | 本地 API 驱动 | 调用数据库本地 C/C++ 库 | 兼容性差,极少用 |
| 3 | 网络协议驱动 | 中间件转发请求 | 企业级专用 |
| 4 | 纯 Java 驱动 | 100% Java 实现,直接通信 | 主流(MySQL、Oracle 均用此类型) |
开发中100% 使用第 4 类驱动。
4. 核心依赖(以 MySQL 8.x 为例)
Maven 依赖(必须对应数据库版本):
<!-- MySQL 8.0+ 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
驱动类全限定名:
- MySQL 8.x:
com.mysql.cj.jdbc.Driver - MySQL 5.x:
com.mysql.jdbc.Driver
二、JDBC 核心 API(必须背熟)
所有操作都围绕以下 6 个核心接口/类:
| 类/接口 | 包路径 | 核心作用 |
|---|---|---|
| DriverManager | java.sql | 加载驱动、获取数据库连接 |
| Connection | java.sql | 数据库连接对象,管理事务、创建 Statement |
| Statement | java.sql | 执行静态 SQL 语句 |
| PreparedStatement | java.sql | 执行预编译 SQL(推荐) |
| CallableStatement | java.sql | 调用存储过程/函数 |
| ResultSet | java.sql | 封装查询结果集 |
| ResultSetMetaData | java.sql | 获取结果集元数据(列名、类型、数量) |
| DataSource | javax.sql | 连接池数据源(企业级标准) |
三、JDBC 标准执行流程(固定 7 步)
这是 JDBC 最基础、最核心的执行模板,所有操作都遵循此流程:
- 加载驱动(JDBC 4.0 后可自动加载)
- 获取数据库连接(Connection)
- 创建语句对象(Statement/PreparedStatement)
- 编写并执行 SQL
- 处理结果集(查询操作)
- 关闭资源(逆序关闭)
- 异常处理
标准代码示例(原生 JDBC)
import java.sql.*;
public class JdbcDemo {
public static void main(String[] args) {
// 1. 数据库连接信息
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true";
String user = "root";
String password = "123456";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 编写 SQL(预编译)
String sql = "SELECT id, name, age FROM user WHERE age > ?";
pstmt = conn.prepareStatement(sql);
// 4. 设置参数
pstmt.setInt(1, 18);
// 5. 执行查询
rs = pstmt.executeQuery();
// 6. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id + "-" + name + "-" + age);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 关闭资源(逆序关闭:ResultSet → Statement → Connection)
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
四、JDBC URL 格式(数据库连接串)
通用格式
jdbc:数据库类型://主机:端口/数据库名?参数1=值&参数2=值
主流数据库 URL
- MySQL 8.x
jdbc:mysql://localhost:3306/数据库名?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf8
- Oracle
jdbc:oracle:thin:@localhost:1521:ORCL
- SQLServer
jdbc:sqlserver://localhost:1433;DatabaseName=数据库名
MySQL 关键 URL 参数(必须掌握)
| 参数 | 作用 |
|---|---|
| useSSL | 关闭 SSL 连接(开发环境) |
| serverTimezone | 设置时区(UTC/Asia/Shanghai) |
| allowPublicKeyRetrieval | 允许公钥获取(解决连接报错) |
| useUnicode | 使用 Unicode 编码 |
| characterEncoding | 字符集 utf8 |
| autoReconnect | 自动重连 |
五、Connection 连接对象(核心)
1. 核心功能
- 获取语句对象(
createStatement/prepareStatement) - 事务管理(提交、回滚、设置隔离级别)
- 关闭连接
2. 事务相关方法
// 关闭自动提交(开启事务)
conn.setAutoCommit(false);
// 提交事务
conn.commit();
// 回滚事务
conn.rollback();
// 设置事务隔离级别
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
// 设置只读(优化性能)
conn.setReadOnly(true);
3. 事务隔离级别(4 级)
从低到高:
TRANSACTION_NONE:不支持事务TRANSACTION_READ_UNCOMMITTED:读未提交(脏读)TRANSACTION_READ_COMMITTED:读已提交(Oracle 默认)TRANSACTION_REPEATABLE_READ:可重复读(MySQL 默认)TRANSACTION_SERIALIZABLE:串行化(最高,性能最低)
并发问题:
- 脏读:读到未提交数据
- 不可重复读:同一事务两次查询结果不一致
- 幻读:范围查询出现新增数据
六、Statement 家族(执行 SQL)
1. Statement(静态 SQL)
- 执行无参数的静态 SQL
- 缺点:SQL 注入风险、性能低、拼接繁琐
- 不推荐生产使用
Statement stmt = conn.createStatement();
// 执行增删改
int rows = stmt.executeUpdate("DELETE FROM user WHERE id=1");
// 执行查询
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
2. PreparedStatement(预编译 SQL,重点)
JDBC 首选语句对象,解决 Statement 所有问题:
- 预编译,性能更高
- 参数化,彻底防止 SQL 注入
- 代码简洁,无需拼接 SQL
核心方法
// 创建对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数(索引从 1 开始)
pstmt.setInt(1, 10);
pstmt.setString(2, "张三");
pstmt.setDate(3, new Date(System.currentTimeMillis()));
// 执行
pstmt.executeUpdate(); // 增删改,返回受影响行数
pstmt.executeQuery(); // 查询,返回 ResultSet
pstmt.execute(); // 通用执行(了解)
批量执行(Batch)
pstmt = conn.prepareStatement("INSERT INTO user(name) VALUES(?)");
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "name" + i);
pstmt.addBatch(); // 添加到批量
}
pstmt.executeBatch(); // 执行批量
3. CallableStatement(调用存储过程)
用于执行数据库存储过程/函数:
// 调用存储过程
CallableStatement cstmt = conn.prepareCall("{CALL 存储过程名(?,?)}");
cstmt.setInt(1, 10);
cstmt.registerOutParameter(2, Types.VARCHAR); // 注册输出参数
cstmt.execute();
String result = cstmt.getString(2);
七、ResultSet 结果集(查询数据)
1. 作用
封装 SELECT 查询返回的所有数据,以行为单位遍历。
2. 遍历方式
// 向下移动一行,返回是否有数据
while (rs.next()) {
// 获取数据:按列名 / 按列索引(从 1 开始)
int id = rs.getInt("id");
String name = rs.getString(2);
}
3. 常用获取方法
getInt()/getLong()getString()getDouble()getDate()/getTime()/getTimestamp()getObject():通用获取
4. 结果集类型(并发/滚动)
创建 Statement 时指定:
// 可滚动、敏感更新、只读
conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY
);
- 滚动:前后移动、跳行
- 敏感:数据变更是否实时反映到结果集
八、ResultSetMetaData 元数据
获取结果集结构信息,无需知道表结构:
ResultSetMetaData meta = rs.getMetaData();
// 列数量
int count = meta.getColumnCount();
// 列名
String name = meta.getColumnName(i);
// 列类型
String type = meta.getColumnTypeName(i);
九、JDBC 事务管理(重点)
1. 事务特性(ACID)
- 原子性:要么全成功,要么全失败
- 一致性:数据始终合法
- 隔离性:多事务互不干扰
- 持久性:提交后永久生效
2. JDBC 手动事务模板
Connection conn = null;
try {
conn = getConnection();
// 关闭自动提交
conn.setAutoCommit(false);
// 执行多个 SQL 操作
update1();
update2();
// 提交
conn.commit();
} catch (Exception e) {
// 异常回滚
if (conn != null) conn.rollback();
e.printStackTrace();
} finally {
close(conn);
}
十、数据库连接池(企业级必备)
1. 为什么要用连接池
原生 JDBC 缺点:
- 频繁创建/关闭连接,性能极差
- 资源消耗大,容易导致数据库崩溃
连接池优势:
- 连接复用
- 控制最大连接数
- 提升性能、稳定性
2. 主流连接池
- HikariCP(SpringBoot 默认,性能最高)
- Druid(阿里,监控+风控)
- C3P0(老旧)
- DBCP(老旧)
3. DataSource 接口
连接池标准接口,企业开发必须用 DataSource 替代 DriverManager。
Druid 连接池示例:
import com.alibaba.druid.pool.DruidDataSource;
public class DruidUtil {
private static DruidDataSource dataSource;
static {
dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql:///testdb");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 核心配置
dataSource.setInitialSize(5); // 初始连接
dataSource.setMaxActive(20); // 最大连接
dataSource.setMinIdle(5); // 最小空闲
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
十一、JDBC 工具类封装(实战必备)
原生代码重复冗余,必须封装工具类:
import java.sql.*;
public class JdbcUtil {
private static final String URL = "jdbc:mysql:///testdb?serverTimezone=UTC";
private static final String USER = "root";
private static final String PWD = "123456";
// 静态加载驱动
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
// 获取连接
public static Connection getConn() {
try {
return DriverManager.getConnection(URL, USER, PWD);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
// 关闭资源
public static void close(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
十二、JDBC 批量处理(性能优化)
1. 适用场景
批量插入/更新大量数据(1000+ 条)。
2. 两种方式
- Statement 批量(不推荐)
- PreparedStatement 批量(推荐)
3. 开启 MySQL 批处理优化
URL 添加参数:
rewriteBatchedStatements=true
4. 批量代码
conn.setAutoCommit(false);
String sql = "INSERT INTO user(name) VALUES(?)";
pstmt = conn.prepareStatement(sql);
for (int i = 0; i < 1000; i++) {
pstmt.setString(1, "test" + i);
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
十三、JDBC 常见异常与解决方案
1. ClassNotFoundException
- 原因:未导入驱动包 / 驱动类名写错
- 解决:检查 Maven 依赖、驱动全类名
2. SQLException: Access denied
- 原因:账号/密码错误
- 解决:核对用户名密码
3. SQLException: Timezone
- 原因:未指定时区
- 解决:URL 加
serverTimezone=UTC
4. SQL 注入
- 原因:使用 Statement 拼接 SQL
- 解决:必须用 PreparedStatement
5. 连接泄漏
- 原因:未关闭 Connection
- 解决:finally 关闭 / 使用连接池
十四、JDBC 高级特性
1. 数据库主从复制(读写分离)
- 写:主库
- 读:从库
- 通过动态切换 DataSource 实现
2. 分布式事务
跨多个数据库的事务,使用:
- Seata
- XA 协议
- @GlobalTransactional
3. 大对象操作
- CLOB:存储大文本
- BLOB:存储二进制(图片、文件)
pstmt.setBlob(1, new FileInputStream("file.jpg"));
4. 滚动结果集
支持结果集向前、向后、跳行查询。
十五、JDBC 性能最佳实践(面试+实战)
- 必须使用 PreparedStatement,禁止 Statement
- 必须使用连接池,禁止原生 DriverManager
- 必须关闭资源,使用 try-with-resources 自动关闭
- 批量操作必须开启
rewriteBatchedStatements - 查询只查需要的列,禁止
SELECT * - 事务控制粒度尽可能小
- 合理设置连接池大小(CPU 核心数 * 2 + 磁盘数)
- 使用索引优化 SQL
十六、Java 7+ try-with-resources 优雅关闭资源
JDBC 实现了 AutoCloseable 接口,可自动关闭:
// 无需手动 finally 关闭,自动释放
try (Connection conn = JdbcUtil.getConn();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
// 业务逻辑
} catch (SQLException e) {
e.printStackTrace();
}
十七、JDBC 与 ORM 框架关系
- JDBC:底层标准,所有 ORM 框架都基于 JDBC
- MyBatis:半自动化 ORM,手写 SQL
- Hibernate/JPA:全自动化 ORM
- Spring JdbcTemplate:JDBC 封装工具类
总结(核心必背)
- JDBC 是 Java 操作数据库的标准 API,核心是接口 + 驱动实现
- 固定流程:加载驱动 → 获取连接 → 创建语句 → 执行 SQL → 处理结果 → 关闭资源
- PreparedStatement 是最优选择,防注入、高性能
- 连接池是生产环境必备,推荐 HikariCP/Druid
- 事务通过 Connection 管理:关闭自动提交 → 提交/回滚
- 资源必须关闭,推荐 try-with-resources
- 性能优化:批量处理、只查需要列、连接复用