一、JDBC
- JDBC 全称 Java DataBase Connectivity,是 Java 数据库连接规范,用于通过 Java 代码操作数据库
- JDBC 是一套接口规范,实现类由各数据库厂商提供
- 数据库驱动是厂商提供的实现类,使用 MySQL 需导入 mysql-connector-java 驱动 jar 包。
- JDBC 核心作用是建立 Java 程序与数据库的连接,实现数据增删改查操作
二、JDBC 快速入门开发步骤
- 创建数据库与数据表
- 创建数据库:
create database jdbcdemo; - 使用数据库:
use jdbcdemo; - 创建用户表:
- 创建数据库:
sql
create table t_user(
id int primary key auto_increment,
username varchar(30),
password varchar(30),
email varchar(30)
);
- 插入测试数据:
sql
insert into t_user values (null,'aaa','123','aaa@163.com');
insert into t_user values (null,'bbb','456','bb@163.com');
insert into t_user values (null,'ccc','789','ccc@163.com');
- 编写 Java 代码实现查询
- 加载驱动
- 获取数据库连接
- 编写 SQL 语句
- 获取执行 SQL 的对象
- 执行 SQL 并处理结果集
- 释放资源
查询代码:
java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JdbcSelectDemo {
public static void main(String[] args) throws Exception {
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
String url = "jdbc:mysql:///jdbcdemo";
Connection conn = DriverManager.getConnection(url, "root", "root");
// 3.获取执行SQL对象
Statement stmt = conn.createStatement();
// 4.编写并执行SQL
String sql = "select * from t_user";
ResultSet rs = stmt.executeQuery(sql);
// 5.遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String username = rs.getString("username");
String password = rs.getString("password");
String email = rs.getString("email");
System.out.println(id + " " + username + " " + password + " " + email);
}
// 6.释放资源
rs.close();
stmt.close();
conn.close();
}
}
三、API
(一)DriverManager 类
- 作用:管理 JDBC 驱动,获取数据库连接。
- 加载驱动
Class.forName("com.mysql.jdbc.Driver");- 避免直接使用 registerDriver 方法,防止驱动重复加载、降低耦合。
- 获取连接
- 方法:
static Connection getConnection(String url, String user, String password) - URL 格式:
jdbc:mysql://localhost:3306/数据库名 - 本地简写:
jdbc:mysql:///数据库名 - 参数:用户名、数据库密码
- 方法:
DriverManager 获取连接:
java
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql:///jdbcdemo";
Connection conn = DriverManager.getConnection(url, "root", "root");
(二)Connection 接口
- 作用:代表数据库连接,资源稀有,使用后必须释放。
- 获取执行 SQL 对象
Statement createStatement():普通执行对象PreparedStatement prepareStatement(String sql):预编译执行对象,防 SQL 注入
- 事务管理
setAutoCommit(boolean autoCommit):设置事务自动提交commit():提交事务rollback():回滚事务
Connection 获取执行对象:
java
// 获取普通Statement
Statement stmt = conn.createStatement();
// 获取预编译PreparedStatement
String sql = "select * from t_user where username=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
(三)Statement 接口
- 作用:执行静态 SQL 语句。
- 核心方法
executeQuery(String sql):执行查询语句,返回 ResultSet 结果集executeUpdate(String sql):执行增删改语句,返回受影响行数
- 支持批处理操作:addBatch、clearBatch、executeBatch
Statement 增删改代码
java
// 添加
String sqlInsert = "insert into t_user values(null,'test','123','test@qq.com')";
int rowsInsert = stmt.executeUpdate(sqlInsert);
// 修改
String sqlUpdate = "update t_user set password='888' where username='aaa'";
int rowsUpdate = stmt.executeUpdate(sqlUpdate);
// 删除
String sqlDelete = "delete from t_user where id=3";
int rowsDelete = stmt.executeUpdate(sqlDelete);
(四)PreparedStatement 接口
- 是 Statement 子接口,支持预编译 SQL,解决 SQL 注入漏洞。
- 使用占位符?替代参数,SQL 先编译后传参。
- 常用方法
setXxx(参数位置, 参数值):为占位符赋值executeQuery():执行查询executeUpdate():执行增删改
PreparedStatement 完整代码:
java
String sql = "insert into t_user values(null,?,?,?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 设置参数
pstmt.setString(1, "preUser");
pstmt.setString(2, "prePwd");
pstmt.setString(3, "pre@qq.com");
// 执行
int rows = pstmt.executeUpdate();
(五)ResultSet 接口
- 作用:封装查询结果集,以表格形式存储数据。
- 核心方法
next():向下移动游标,判断是否有下一行数据getXxx(字段名/下标):获取指定字段值,下标从 1 开始getObject():获取任意类型数据,需自行强转
ResultSet 遍历:
java
ResultSet rs = stmt.executeQuery("select * from t_user");
while (rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("username");
System.out.println(id + "---" + name);
}
四、资源释放
- 连接、执行对象、结果集使用后必须释放。
- 释放代码放在 finally 代码块中,保证一定执行。
- 释放顺序:ResultSet → Statement → Connection。
- 释放前判断对象非空,避免空指针异常。
java
ResultSet rs = null;
Statement stmt = null;
Connection conn = null;
try {
// 数据库操作
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放
if (rs != null) {
try { rs.close(); } catch (Exception e) { e.printStackTrace(); }
}
if (stmt != null) {
try { stmt.close(); } catch (Exception e) { e.printStackTrace(); }
}
if (conn != null) {
try { conn.close(); } catch (Exception e) { e.printStackTrace(); }
}
}
五、SQL 注入问题
- 漏洞原理:拼接 SQL 语句时,用户输入特殊字符改变 SQL 逻辑。
- 典型注入语句:
aaa' or '1=1、aaa' -- ' - 危害:已知用户名可任意登录,绕过密码验证。
- 解决方案:使用 PreparedStatement 预编译对象。
SQL 注入演示:
java
// 危险:字符串拼接SQL
String username = "aaa' or '1=1";
String password = "任意密码";
String sql = "select * from t_user where username='" + username + "' and password='" + password + "'";
解决方法(PreparedStatement):
java
String sql = "select * from t_user where username=? and password=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
六、数据库事务
(一)事务
- 事务是数据库操作的最小单元,要么全部成功,要么全部失败。
- 典型场景:银行转账,扣款与加款必须同时成功或失败。
事务测试表 SQL:
java
create table t_account(
id int primary key auto_increment,
username varchar(20),
money double
);
insert into t_account values (null,'美美',10000);
insert into t_account values (null,'冠希',10000);
(二)MySQL 事务操作
- 命令行方式
- 开启事务:
start transaction; - 执行 SQL 语句
- 提交事务:
commit; - 回滚事务:
rollback;
- 开启事务:
- 设置非自动提交
- 关闭自动提交:
set autocommit = off; - 手动执行 commit 或 rollback
- 关闭自动提交:
(三)JDBC 事务操作
- 核心依赖 Connection 接口。
- 步骤
- 关闭自动提交:
conn.setAutoCommit(false); - 执行多个 SQL 操作
- 正常执行:
conn.commit(); - 出现异常:
conn.rollback();
- 关闭自动提交:
JDBC 转账事务:
java
public void transfer() {
Connection conn = null;
PreparedStatement p1 = null;
PreparedStatement p2 = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///jdbcdemo", "root", "root");
// 开启事务
conn.setAutoCommit(false);
// 冠希 -1000
String sql1 = "update t_account set money=money-1000 where username='冠希'";
p1 = conn.prepareStatement(sql1);
p1.executeUpdate();
// 美美 +1000
String sql2 = "update t_account set money=money+1000 where username='美美'";
p2 = conn.prepareStatement(sql2);
p2.executeUpdate();
// 提交
conn.commit();
System.out.println("转账成功");
} catch (Exception e) {
e.printStackTrace();
// 回滚
try {
if (conn != null) conn.rollback();
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("转账失败");
} finally {
// 释放资源
try {
if (p1 != null) p1.close();
if (p2 != null) p2.close();
if (conn != null) conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
(四)事务四大特性(ACID)
- 原子性:事务不可分割,全部执行或全部不执行。
- 一致性:事务执行前后数据总量保持一致。
- 隔离性:并发事务之间相互隔离,不干扰。
- 持久性:事务提交后数据永久保存到数据库。
(五)事务隔离性问题
- 脏读:读取到其他事务未提交的数据。
- 不可重复读:同一事务多次查询结果不一致,针对 update 操作。
- 幻读:同一事务多次查询结果不一致,针对 insert 操作。
(六)四种隔离级别
- Read uncommitted:最低级别,无法解决任何问题。
- Read committed:避免脏读,允许不可重复读、幻读。
- Repeatable read:MySQL 默认级别,避免脏读、不可重复读,允许幻读。
- Serializable:最高级别,避免所有问题,性能最低。
- 安全性:Serializable > Repeatable read > Read committed > Read uncommitted。
- 效率:Serializable < Repeatable read < Read committed < Read uncommitted。
七、数据库连接池
(一)连接池概述
- 解决频繁创建、销毁连接的性能损耗问题。
- 连接可复用,提升程序执行效率。
- 核心参数:初始连接数、最大连接数、最小空闲连接、最大等待时间。
(二)DataSource 接口
- SUN 公司定义标准接口,所有连接池必须实现。
- 核心方法:
Connection getConnection()获取连接。
(三)常用连接池
- DBCP:Apache 开源组织提供。
- C3P0:传统开源连接池。
- Druid:阿里巴巴开发,性能、功能最优,支持监控、加密等。
(四)Druid 连接池使用步骤
- 导入 druid 对应的 jar 包。
- 配置参数
- 驱动类:
driverClassName=com.mysql.jdbc.Driver - 连接地址:
url=jdbc:mysql:///数据库名 - 用户名、密码
- 初始连接数、最大连接数、最大等待时间
- 驱动类:
druid.properties 配置文件:
XML
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///jdbcdemo
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
- 编写工具类
- 静态代码块加载配置文件,创建连接池对象。
- 提供获取连接、释放资源的静态方法。
- 连接释放:调用 close () 方法并非销毁连接,而是归还到连接池。
Druid 工具类:
java
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static DataSource dataSource;
static {
try {
Properties prop = new Properties();
InputStream is = JdbcUtils.class.getResourceAsStream("/druid.properties");
prop.load(is);
dataSource = DruidDataSourceFactory.createDataSource(prop);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws Exception {
return dataSource.getConnection();
}
// 释放资源
public static void close(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Druid 使用测试:
java
public class DruidTest {
public static void main(String[] args) throws Exception {
Connection conn = JdbcUtils.getConnection();
String sql = "insert into t_user values(null,'druid','123','druid@qq.com')";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate();
JdbcUtils.close(null, pstmt, conn);
}
}
八、JDBC 工具类封装
- 作用:简化连接获取、资源释放操作。
- 实现方式
- 配置文件存储数据库参数,解耦代码。
- 静态代码块初始化连接池。
- 提供统一的 getConnection 方法。
- 重载 close 方法,支持不同场景资源释放。
工具类重载 close 方法:
java
// 无结果集
public static void close(Statement stmt, Connection conn) {
close(null, stmt, conn);
}