第一部分:SQL基础(SELECT/INSERT/UPDATE)
掌握SQL核心操作,为JDBC操作打下坚实基础
学习目标
理解并掌握SELECT查询、INSERT插入、UPDATE更新操作的基本语法和执行原理
1
SELECT查询
从数据库表中检索数据:
SELECT column1, column2, ...
FROM table_name
WHERE condition;
-- 示例:查询所有员工信息
SELECT * FROM employees;
-- 示例:查询特定部门的员工
SELECT id, name, department
FROM employees
WHERE department = 'IT';
FROM table_name
WHERE condition;
-- 示例:查询所有员工信息
SELECT * FROM employees;
-- 示例:查询特定部门的员工
SELECT id, name, department
FROM employees
WHERE department = 'IT';
2
INSERT插入
向数据库表中插入新记录:
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);
-- 示例:插入新员工记录
INSERT INTO employees (name, email, department)
VALUES ('张三', 'zhangsan@example.com', 'HR');
VALUES (value1, value2, ...);
-- 示例:插入新员工记录
INSERT INTO employees (name, email, department)
VALUES ('张三', 'zhangsan@example.com', 'HR');
3
UPDATE更新
修改数据库表中的现有记录:
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;
-- 示例:更新员工部门
UPDATE employees
SET department = 'Marketing'
WHERE id = 101;
SET column1 = value1, column2 = value2, ...
WHERE condition;
-- 示例:更新员工部门
UPDATE employees
SET department = 'Marketing'
WHERE id = 101;
警告:UPDATE语句必须使用WHERE子句,否则会更新表中所有记录!
4
SQL操作最佳实践
- 始终使用参数化查询防止SQL注入
- 在UPDATE/DELETE操作前使用SELECT确认影响范围
- 对常用查询列创建索引提高性能
- 使用事务保证多个操作的原子性
- 避免在WHERE子句中对列进行函数操作
第二部分:JDBC连接数据库(DriverManager)
掌握Java程序与数据库建立连接的核心方法
学习目标
理解JDBC架构,掌握使用DriverManager建立数据库连接的方法
Java应用
→
数据库
1
JDBC连接步骤
- 加载数据库驱动
- 使用DriverManager获取Connection对象
- 创建Statement或PreparedStatement
- 执行SQL查询或更新
- 处理结果集(ResultSet)
- 关闭所有资源
2
建立连接代码示例
import java.sql.*;
public class JdbcExample {
public static void main(String[] args) {
// 1. 数据库连接参数
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
Connection conn = null;
try {
// 2. 加载驱动(JDBC 4.0+后自动加载)
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 建立连接
conn = DriverManager.getConnection(url, user, password);
System.out.println("成功连接到数据库!");
}
catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
finally {
// 4. 关闭连接
if (conn != null) {
try { conn.close(); }
catch (SQLException e) { e.printStackTrace(); }
}
}
}
}
public class JdbcExample {
public static void main(String[] args) {
// 1. 数据库连接参数
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
Connection conn = null;
try {
// 2. 加载驱动(JDBC 4.0+后自动加载)
Class.forName("com.mysql.cj.jdbc.Driver");
// 3. 建立连接
conn = DriverManager.getConnection(url, user, password);
System.out.println("成功连接到数据库!");
}
catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
finally {
// 4. 关闭连接
if (conn != null) {
try { conn.close(); }
catch (SQLException e) { e.printStackTrace(); }
}
}
}
}
连接池的重要性
在生产环境中,建议使用连接池(如HikariCP、Apache DBCP)管理数据库连接。连接池可以:
- 减少频繁创建连接的开销
- 控制最大连接数防止资源耗尽
- 提供连接健康检查机制
- 支持自动重连功能
第三部分:Statement与PreparedStatement
掌握执行SQL语句的两种核心接口及其适用场景
学习目标
理解Statement与PreparedStatement的区别,掌握PreparedStatement防止SQL注入的原理
特性 | Statement | PreparedStatement |
---|---|---|
SQL注入防护 | ❌ 不安全 | ✅ 安全 |
性能 | 每次执行都需要编译 | 预编译,多次执行效率高 |
参数设置 | 字符串拼接(易出错) | 使用setXxx()方法 |
适用场景 | DDL操作、无参数SQL | 带参数的DML操作 |
二进制数据处理 | 不直接支持 | 支持BLOB/CLOB |
1
PreparedStatement使用示例
// 使用PreparedStatement安全查询
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数(防止SQL注入)
pstmt.setString(1, username);
pstmt.setString(2, password);
// 执行查询
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
// 登录成功
}
}
}
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
// 设置参数(防止SQL注入)
pstmt.setString(1, username);
pstmt.setString(2, password);
// 执行查询
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
// 登录成功
}
}
}
2
PreparedStatement最佳实践
- 始终使用try-with-resources确保资源关闭
- 为每个参数使用正确的setXxx方法(setString, setInt等)
- 批量操作时使用addBatch()和executeBatch()
- 重用PreparedStatement对象提高性能
- 使用setObject()处理不确定类型的数据
常见错误:在循环中创建PreparedStatement,导致大量对象创建,应复用PreparedStatement
第四部分:事务处理(ACID特性)
确保数据库操作的原子性、一致性、隔离性和持久性
学习目标
理解事务的ACID特性,掌握JDBC中事务管理的方法
原子性 (Atomicity)
事务中的所有操作要么全部成功,要么全部失败回滚
一致性 (Consistency)
事务执行前后,数据库必须保持一致状态
隔离性 (Isolation)
多个并发事务之间互相隔离,互不干扰
持久性 (Durability)
事务提交后,对数据库的改变是永久性的
1
JDBC事务管理代码
Connection conn = null;
try {
conn = dataSource.getConnection();
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 执行多个SQL操作
updateAccount(conn, "A", -100.0);
updateAccount(conn, "B", 100.0);
// 提交事务
conn.commit();
} catch (SQLException e) {
if (conn != null) {
try {
// 回滚事务
conn.rollback();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 关闭连接
if (conn != null) {
try { conn.close(); }
catch (SQLException e) { e.printStackTrace(); }
}
}
try {
conn = dataSource.getConnection();
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
// 执行多个SQL操作
updateAccount(conn, "A", -100.0);
updateAccount(conn, "B", 100.0);
// 提交事务
conn.commit();
} catch (SQLException e) {
if (conn != null) {
try {
// 回滚事务
conn.rollback();
}
catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 关闭连接
if (conn != null) {
try { conn.close(); }
catch (SQLException e) { e.printStackTrace(); }
}
}
2
事务隔离级别
JDBC支持的事务隔离级别:
- TRANSACTION_READ_UNCOMMITTED - 读未提交
- TRANSACTION_READ_COMMITTED - 读已提交(默认)
- TRANSACTION_REPEATABLE_READ - 可重复读
- TRANSACTION_SERIALIZABLE - 序列化
设置隔离级别
在事务开始前设置:
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
第五部分:DAO设计模式
实现数据访问逻辑与业务逻辑的分离
学习目标
掌握DAO设计模式的核心概念和实现方法
DAO模式结构
- DAO接口 - 定义数据访问方法
- DAO实现类 - 具体的数据库操作
- 实体类 - 映射数据库表结构
- 业务逻辑层 - 调用DAO进行业务处理
DAO模式优点
- 分离数据访问逻辑与业务逻辑
- 提高代码可维护性和可测试性
- 便于切换数据源(SQL/NoSQL)
- 实现数据访问的统一接口
1
DAO接口定义
public interface UserDao {
User getUserById(int id);
List<User> getAllUsers();
void addUser(User user);
void updateUser(User user);
void deleteUser(int id);
}
User getUserById(int id);
List<User> getAllUsers();
void addUser(User user);
void updateUser(User user);
void deleteUser(int id);
}
2
DAO实现类示例
public class UserDaoImpl implements UserDao {
private DataSource dataSource;
// 构造函数注入DataSource
public UserDaoImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public User getUserById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
return new User(
rs.getInt("id"),
rs.getString("username"),
rs.getString("email"));
}
}
catch (SQLException e) {
throw new DataAccessException("Error fetching user", e);
}
return null;
}
// 其他方法实现...
}
private DataSource dataSource;
// 构造函数注入DataSource
public UserDaoImpl(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public User getUserById(int id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
return new User(
rs.getInt("id"),
rs.getString("username"),
rs.getString("email"));
}
}
catch (SQLException e) {
throw new DataAccessException("Error fetching user", e);
}
return null;
}
// 其他方法实现...
}
DAO模式最佳实践
- 为每个实体创建独立的DAO接口和实现
- 使用依赖注入(如Spring)管理DAO依赖
- 在DAO层处理SQL异常,转换为业务异常
- 使用泛型创建基础DAO接口减少重复代码
- 结合Spring的JdbcTemplate简化JDBC操作
第六部分:综合复习测试题
检验SQL与JDBC知识的掌握程度
1
SQL基础题
给定一个员工表(employees)结构如下:
id INT PRIMARY KEY,
name VARCHAR(50),
department VARCHAR(50),
salary DECIMAL(10,2)
请编写SQL语句完成以下操作:
a) 查询市场部(Marketing)所有员工的姓名和工资
b) 将技术部(IT)所有员工的工资增加10%
c) 插入一条新员工记录:姓名-李四,部门-销售(Sales),工资-8000
id INT PRIMARY KEY,
name VARCHAR(50),
department VARCHAR(50),
salary DECIMAL(10,2)
请编写SQL语句完成以下操作:
a) 查询市场部(Marketing)所有员工的姓名和工资
b) 将技术部(IT)所有员工的工资增加10%
c) 插入一条新员工记录:姓名-李四,部门-销售(Sales),工资-8000
2
JDBC连接题
以下JDBC代码存在哪些问题?请指出并修正:
public void getUser(String username) {
Connection conn = null;
Statement stmt = null;
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
try {
conn = DriverManager.getConnection(DB_URL, USER, PASS);
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println("User: " + rs.getString("username"));
}
}
catch (SQLException e) {
e.printStackTrace();
}
}
Connection conn = null;
Statement stmt = null;
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
try {
conn = DriverManager.getConnection(DB_URL, USER, PASS);
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println("User: " + rs.getString("username"));
}
}
catch (SQLException e) {
e.printStackTrace();
}
}
3
事务处理题
在一个银行转账场景中,需要从账户A向账户B转账100元:
a) 使用JDBC代码实现转账事务
b) 解释为什么需要事务处理
c) 如果第二步操作失败会发生什么?
a) 使用JDBC代码实现转账事务
b) 解释为什么需要事务处理
c) 如果第二步操作失败会发生什么?
4
DAO设计题
设计一个ProductDAO接口,要求包含以下功能:
- 根据ID获取产品
- 获取所有产品列表
- 添加新产品
- 更新产品信息
- 根据ID删除产品
请:
a) 定义ProductDAO接口
b) 编写一个方法实现获取所有产品列表
- 根据ID获取产品
- 获取所有产品列表
- 添加新产品
- 更新产品信息
- 根据ID删除产品
请:
a) 定义ProductDAO接口
b) 编写一个方法实现获取所有产品列表
第七部分:学习总结
巩固SQL与JDBC核心知识,为后续学习打下基础
关键知识点回顾
- SQL基础:SELECT查询、INSERT插入、UPDATE更新
- JDBC核心:DriverManager建立连接、Statement/PreparedStatement执行SQL
- ResultSet:遍历处理查询结果
- 事务管理:ACID特性、commit/rollback操作
- DAO模式:数据访问层抽象,分离业务与数据逻辑
常见问题总结
问题类型 | 描述 | 解决方案 |
---|---|---|
连接问题 | 无法建立数据库连接 | 检查URL、用户名、密码;确认数据库服务运行;检查网络连接 |
SQL注入 | 使用字符串拼接导致的安全漏洞 | 始终使用PreparedStatement进行参数化查询 |
资源泄露 | 未关闭Connection、Statement等资源 | 使用try-with-resources自动关闭资源 |
事务管理 | 未处理事务导致数据不一致 | 正确使用commit/rollback;设置合适的隔离级别 |
性能问题 | 频繁创建数据库连接 | 使用连接池管理数据库连接 |