1.JDBC简介
一、关系数据库基础
-
数据存储需求
- 程序数据需持久化存储,自定义格式存在读写复杂、查询低效等问题,数据库应运而生。
-
关系模型核心
- 基于表结构存储数据,通过外键关联实现表间 "一对多" 关系。
- 使用 SQL 语句查询数据,例:
SELECT * FROM classes WHERE grade_id = '1'
。
二、NoSQL 与 SQL 对比
- NoSQL 虽宣传速度和规模优势,但 SQL 是数据库基础,理解 SQL 是掌握 NoSQL 的前提,不可忽视。
三、数据库类别
-
付费商用数据库
- 代表:Oracle、SQL Server、DB2、Sybase。
- 特点:不开源、需付费,适合需要厂商技术支持的企业场景。
-
免费开源数据库
-
推荐:
- MySQL:普及率高、工具丰富,适合 Web 开发。
- PostgreSQL:学术性强,功能强大但知名度较低。
- SQLite:轻量级,适合嵌入式系统和桌面应用。
-
四、MySQL 安装与配置要点
-
版本选择
- 下载免费的MySQL Community Server版本。
-
编码配置
-
Windows:安装时直接选择 UTF-8 编码。
-
Mac/Linux :修改配置文件
/etc/my.cnf
或/etc/mysql/my.cnf
,添加以下内容:ini[client] default-character-set = utf8 [mysqld] default-storage-engine = INNODB character-set-server = utf8 collation-server = utf8_general_ci
-
高版本优化 :若 MySQL≥5.5.3,可设置
utf8mb4
编码(兼容 utf8,支持 emoji 等字符)。
-
-
编码验证
- 登录 MySQL 命令行,执行
show variables like '%char%'
,检查结果是否包含utf8
字样。
- 登录 MySQL 命令行,执行
五、JDBC 核心内容
-
定义
- Java Database Connectivity 的缩写,是 Java 程序访问数据库的标准接口,位于
java.sql
包(JDK 标准库)。
- Java Database Connectivity 的缩写,是 Java 程序访问数据库的标准接口,位于
-
架构原理
- Java 程序 通过 JDBC 接口调用数据库,具体实现由数据库厂商提供的 JDBC 驱动(如 MySQL 的 jar 包)完成。
- 驱动功能:封装网络通讯细节,实现与数据库的底层交互。
-
核心优势
- 接口统一:各数据库厂商遵循相同接口,Java 代码无需针对不同数据库单独开发。
- 低依赖 :编译期仅依赖
java.sql
包,运行时动态加载数据库驱动 jar 包。 - 易迁移:更换数据库时,只需替换驱动 jar 包,核心代码改动极小。
2.JDBC查询
一、JDBC 基础概念
-
JDBC 接口与驱动
-
JDBC 是 Java 标准库
java.sql
中的接口规范,用于连接数据库。 -
数据库厂商需提供 JDBC 驱动(实现接口的 jar 包),如 MySQL 驱动
mysql-connector-java
。 -
Maven 依赖配置:
xml<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> <!-- 仅运行时需要,避免编译期混淆 --> </dependency>
-
-
数据库准备
- 通过 SQL 脚本创建数据库、表及初始数据(以 MySQL 为例),需注意 MySQL 8.x 版本的用户创建语句差异。
二、连接数据库(Connection)
-
URL 格式
-
示例(MySQL):
javajdbc:mysql://localhost:3306/learnjdbc?useSSL=false&characterEncoding=utf8
- 参数说明:
hostname
(主机名)、port
(端口,默认 3306)、db
(数据库名),useSSL
关闭加密,characterEncoding
指定字符编码。
- 参数说明:
-
-
获取连接
-
使用
DriverManager.getConnection(url, user, password)
获取连接。 -
资源管理最佳实践 :通过
try (resource)
自动释放连接,避免资源泄漏。javatry (Connection conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD)) { // 操作数据库 } // 自动关闭连接
-
三、执行 JDBC 查询
-
查询步骤
- 步骤 1 :创建
Statement
或PreparedStatement
对象。 - 步骤 2 :执行查询语句,返回
ResultSet
结果集。 - 步骤 3 :遍历
ResultSet
读取数据。
- 步骤 1 :创建
-
Statement vs PreparedStatement
-
Statement :直接拼接 SQL,存在SQL 注入风险(如用户输入恶意字符串篡改查询逻辑)。
-
PreparedStatement:
-
使用
?
作为占位符,避免 SQL 注入。 -
示例:
javaString sql = "SELECT * FROM students WHERE gender=? AND grade=?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setObject(1, 1); // 设置参数(索引从1开始) ps.setObject(2, 3); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { long id = rs.getLong("id"); // 按列名读取更易读 String name = rs.getString("name"); } } }
-
优势:安全、高效(可复用数据库查询缓存)。
-
-
-
ResultSet 操作要点
-
rs.next()
:移动到下一行,返回boolean
判断是否有数据(初始位置在无数据行 ,需先调用next()
)。 -
列索引从
1
开始,可通过列名(如rs.getString("name")
)或索引读取数据。 -
聚合查询(如
SUM(score)
)结果仍通过ResultSet
读取:javaif (rs.next()) { double sum = rs.getDouble(1); // 读取第一列 }
-
四、SQL 注入防护
- 核心问题 :拼接 SQL 时,用户输入的恶意字符串可能篡改查询逻辑(如
" OR 1=1 --"
绕过验证)。 - 解决方案 :必须使用
PreparedStatement
,禁止拼接 SQL 字符串。
五、数据类型映射
SQL 数据类型 | Java 数据类型 |
---|---|
BIT , BOOL |
boolean |
INTEGER |
int |
BIGINT |
long |
REAL |
float |
FLOAT , DOUBLE |
double |
CHAR , VARCHAR |
String |
DECIMAL |
BigDecimal |
DATE |
java.sql.Date , LocalDate |
TIME |
java.sql.Time , LocalTime |
- 注意 :仅最新 JDBC 驱动支持
LocalDate
/LocalTime
。
3.JDBC更新
(一)CRUD 概念
数据库操作主要包括增(Create)、删(Delete)、改(Update)、查(Retrieve),简称 CRUD,本文重点介绍增、改、删操作(更新操作)。
(二)插入操作(INSERT)
-
实现方式:使用 PreparedStatement 执行 INSERT 语句,通过 executeUpdate () 方法执行,返回值为插入的记录数量(通常为 1)。
-
示例代码要点:
- 设置参数时,占位符 "?" 的索引从 1 开始,通过 setObject () 等方法设置对应参数。
- 示例代码:
javatry (Connection conn = DriverManager.getConnection(...)) { try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (...) VALUES (?,?,?,?)")) { ps.setObject(1, 999); ps.setObject(2, 1); ps.setObject(3, "Bob"); ps.setObject(4, "M"); int n = ps.executeUpdate(); // 返回1 } }
-
注意事项:严格避免手动拼 SQL 字符串,防止安全漏洞。
(三)插入并获取自增主键
-
适用场景:当表设置自增主键时,插入后需获取自动生成的主键值。
-
关键步骤:
- 创建 PreparedStatement 时,指定 Statement.RETURN_GENERATED_KEYS 标志位,告知 JDBC 驱动返回自增主键。
- 执行 executeUpdate () 后,通过 getGeneratedKeys () 获取 ResultSet 对象,从中读取主键值。
-
示例代码要点:
javatry (Connection conn = ...) { try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (...) VALUES (?,?,?)", Statement.RETURN_GENERATED_KEYS)) { // 设置参数 int n = ps.executeUpdate(); try (ResultSet rs = ps.getGeneratedKeys()) { if (rs.next()) { long id = rs.getLong(1); // 索引从1开始 } } } }
-
注意事项:若一次插入多条记录,ResultSet 可能有多行;自增列不一定是主键,可能有多个自增列。
(四)更新操作(UPDATE)
-
实现方式:使用 PreparedStatement 执行 UPDATE 语句,通过 executeUpdate () 方法执行,返回值为实际更新的行数(可能为正数或 0)。
-
示例代码要点:
javatry (Connection conn = ...) { try (PreparedStatement ps = conn.prepareStatement("UPDATE students SET name=? WHERE id=?")) { ps.setObject(1, "Bob"); ps.setObject(2, 999); int n = ps.executeUpdate(); // 返回更新的行数 } }
(五)删除操作(DELETE)
-
实现方式:使用 PreparedStatement 执行 DELETE 语句,通过 executeUpdate () 方法执行,返回值为删除的行数。
-
示例代码要点:
javatry (Connection conn = ...) { try (PreparedStatement ps = conn.prepareStatement("DELETE FROM students WHERE id=?")) { ps.setObject(1, 999); int n = ps.executeUpdate(); // 返回删除的行数 } }
4. JDBC事务
一、数据库事务基础
-
定义
由若干 SQL 语句构成的操作序列,确保所有操作要么全部执行成功,要么全部回滚,类似于 Java 的
synchronized
同步机制。 -
ACID 特性
- 原子性(Atomicity) :事务中的操作要么全成功,要么全失败。
- 一致性(Consistency) :事务执行前后,数据状态保持合法。
- 隔离性(Isolation) :多个事务并发执行时,互不干扰。
- 持久性(Durability) :事务提交后,数据变更永久保存。
二、事务隔离级别
-
SQL 标准定义的 4 种隔离级别
隔离级别 脏读(Dirty Read) 不可重复读(Non Repeatable Read) 幻读(Phantom Read) 读未提交(Read Uncommitted) 是 是 是 读已提交(Read Committed) - 是 是 可重复读(Repeatable Read) - - 是 串行化(Serializable) - - - -
说明
- 脏读:事务 A 读取到事务 B 未提交的数据,若 B 回滚,A 读取的数据无效。
- 不可重复读:事务 A 两次读取同一数据,期间事务 B 修改并提交,导致 A 两次结果不一致。
- 幻读:事务 A 按条件查询数据,事务 B 插入 / 删除符合条件的数据并提交,导致 A 两次查询结果集不同。
- 默认隔离级别 :MySQL 为
REPEATABLE_READ
,其他数据库可能不同。
三、JDBC 事务实现
-
关键步骤
javaConnection conn = openConnection(); try { conn.setAutoCommit(false); // 关闭自动提交,开启事务 // 执行多条SQL语句(如insert、update、delete) conn.commit(); // 提交事务 } catch (SQLException e) { conn.rollback(); // 捕获异常并回滚事务 } finally { conn.setAutoCommit(true); // 恢复自动提交模式 conn.close(); // 关闭连接 }
-
核心方法
setAutoCommit(false)
:关闭自动提交,开始事务。commit()
:提交事务,所有操作生效。rollback()
:回滚事务,撤销所有未提交操作。- 注意 :默认情况下
Connection
处于自动提交模式(每条 SQL 单独作为事务执行),需显式关闭以开启批量事务。
-
设置隔离级别
javaconn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 设置为读已提交级别
- 若未显式设置,使用数据库默认隔离级别。
四、示例与实践
-
场景:转账操作需保证原子性,两条 SQL 必须在同一事务中执行:
sqlUPDATE accounts SET balance = balance - 100 WHERE id = 123 AND balance >= 100; UPDATE accounts SET balance = balance + 100 WHERE id = 456;
- 若第一条成功、第二条失败,事务回滚,避免资金丢失。
5.JDBC批处理
为什么需要 JDBC 批处理?
在 Java 开发中,当需要执行大量相同 SQL 语句(如批量插入、更新)时,循环单条执行会导致频繁的数据库交互,性能低下 。
JDBC 批处理(Batch Processing)通过将多条参数不同的 SQL 合并为一个批次执行,减少网络请求次数和数据库解析开销 ,大幅提升性能。
场景举例:
- 批量插入用户积分记录
- 批量更新商品库存状态
- 批量导入 Excel 数据到数据库
核心实现:三步完成批处理
1. 创建预编译语句(PreparedStatement)
使用带占位符的 SQL 语句,避免重复解析 SQL:
java
String sql = "INSERT INTO students (name, gender, score) VALUES (?, ?, ?)";
PreparedStatement ps = conn.prepareStatement(sql); // conn为数据库连接
2. 循环添加参数到批次
通过addBatch()
方法将不同参数组加入批次,复用同一 PreparedStatement 对象:
java
for (Student student : studentList) {
ps.setString(1, student.getName());
ps.setBoolean(2, student.isGender());
ps.setInt(3, student.getScore());
ps.addBatch(); // 添加到批次
}
3. 执行批次并处理结果
调用executeBatch()
一次性执行所有语句,返回值为每个语句影响的行数数组:
java
int[] updateCounts = ps.executeBatch(); // 执行批次
for (int count : updateCounts) {
System.out.println("影响行数:" + count); // 输出每条语句的执行结果
}
关键优势对比:批处理 vs 单条执行
维度 | 单条执行 | 批处理执行 |
---|---|---|
数据库交互次数 | 每次执行 1 次网络请求 | 整个批次仅 1 次网络请求 |
性能 | 低(尤其数据量大时) | 高(性能提升 5-10 倍) |
代码复杂度 | 简单,但循环次数多 | 需要维护批次逻辑 |
6.JDBC连接池
一、核心概念
-
作用
- 避免频繁创建和销毁 JDBC 连接带来的开销,通过复用连接提高数据库操作效率。
- 类比线程池,减少资源消耗,提升系统性能。
-
标准接口
- 基于
javax.sql.DataSource
接口实现,该接口位于 Java 标准库,仅定义规范,需结合具体实现类使用。
- 基于
-
常用实现
- HikariCP(当前最流行)、C3P0、BoneCP、Druid。
二、HikariCP 使用示例
-
添加依赖
- Maven 依赖:
com.zaxxer:HikariCP:2.7.1
(或其他版本)。
- Maven 依赖:
-
创建连接池
javaHikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); // 数据库URL config.setUsername("root"); // 用户名 config.setPassword("password"); // 密码 // 配置参数 config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒 config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒 config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10 DataSource ds = new HikariDataSource(config); // 创建连接池实例
-
获取与释放连接
javatry (Connection conn = ds.getConnection()) { // 从连接池获取连接 // 执行SQL操作 } // 自动释放连接回连接池(非真正关闭)
三、关键要点
-
全局变量存储
-
DataSource
创建成本高,需作为全局变量贯穿应用生命周期,示例:- 使用静态变量:
static Map<String, Object> global = new HashMap<>(); global.put("dataSource", ds);
- Web 场景可绑定到
ServletContext
。
- 使用静态变量:
-
-
连接复用机制
- 首次获取连接时创建,调用
conn.close()
时释放回池,标记为 "空闲" 而非销毁。
- 首次获取连接时创建,调用
-
配置参数
- 核心参数:最小 / 最大连接数、空闲超时时间、连接超时时间等,需根据负载合理配置。
- 多数连接池支持实时状态监控。