目录
一、MySQL事务操作两种方式
1.1 显式事务控制(命令行方式)
-- 开启事务
START TRANSACTION;
-- 执行转账操作
UPDATE account SET money = money - 1000 WHERE name = '冠希';
UPDATE account SET money = money + 1000 WHERE name = '美美';
-- 根据执行结果选择提交或回滚(提交/回滚后该事务结束)
COMMIT; -- 成功时提交
ROLLBACK; -- 出现异常时回滚
1.2 关闭自动提交模式
-- 查看当前自动提交状态
SHOW VARIABLES LIKE 'autocommit'; -- 默认值为ON
-- 关闭自动提交
SET autocommit = OFF;
-- 执行转账操作
UPDATE account SET money = money - 1000 WHERE name = '冠希';
UPDATE account SET money = money + 1000 WHERE name = '美美';
-- 手动提交
COMMIT;
-- 或回滚操作(只要不提交,该事务可多次执行回滚操作)
ROLLBACK;
-- 恢复默认设置或客户端窗口关闭后再次打开(上次设置自动失效)
SET autocommit = ON;
二、JDBC事务管理实现
package com.qcby.dao;
import com.qcby.utils.JdbcUtils2;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class TransferDao {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
//jdbc连接数据库
//1.加载驱动
//2.获取连接(URL 用户名 密码)
connection = JdbcUtils2.getConnection();
//开启事务(关闭自动提交)
connection.setAutoCommit(false);
//3.编写sql
String sql1="UPDATE account SET money = money - 1000 WHERE name = '冠希'";
String sql2="UPDATE account SET money = money + 1000 WHERE name = '美美'";
//4.获取执行sql的stmt的对象
statement = connection.createStatement();
int i=statement.executeUpdate(sql1);
//int m=10/0;
int i2=statement.executeUpdate(sql2);
//提交事务
connection.commit();
System.out.println("转账成功!");
} catch (SQLException e) {
//回滚事务(如果发生异常)
try {
connection.rollback();
System.out.println("转账失败,已回滚!");
} catch (SQLException e1) {
e1.printStackTrace();
}
System.out.println("转账失败,已回滚!");
e.printStackTrace();
}finally {
//7.关闭资源(先开的后关 后开的先关)
JdbcUtils2.close(connection, statement);
}
}
}
若 int m=10/0; 该行代码正常执行,则返回除零异常,执行回滚操作,数据未发生改变;
若代码正常执行,则返回转账成功,数据库执行对应操作。
三、事务核心特性(ACID)
3.1 事务
事务是进行逻辑处理的最小单元,要么全部成功执行,要么全部失败回滚。
3.2 事务的特性
1. 原子性:Atomicity,即表示事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败;
2. 一致性:Consistency,事务执行后,数据库状态与其它业务规则保持一致。例如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的;
3. 隔离性:Isolation,是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰;
4. 持久性:Durability,指的是一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。
3.3 不考虑事务的隔离性会引发的问题
1. 脏读(Dirty Read)
一个事务读取到了另一个事务未提交的数据。
举例(演示脏读)
条件:READ UNCOMMITTED
操作步骤:
窗口A(事务1) | 窗口B(事务2) |
---|---|
set session transaction isolation level read uncommitted; | start transaction; |
start transaction; | update account set money=money-1000 where name = '熊大'; |
select * from account; → 熊大9000,熊二10000 | update account set money=money+1000 where name = '熊二'; |
此时看到未提交的数据 | rollback; (撤销操作) |
select * from account; → 数据恢复 |
现象:A读取到B未提交的临时数据,导致脏读。
2. 不可重复读
一个事务读取到了另一个事务提交的数据,导致了多次查询的结果不一致。强调的是update,修改记录的数据。
避免脏读和演示不可重复读
操作步骤:
窗口A(事务1) | 窗口B(事务2) |
---|---|
| start transaction; |
| |
select * from account; → 未读到B的修改 | |
commit; | |
select * from account; → 提交后可见 |
现象:脏读被解决,但同一事务中多次查询结果不一致(不可重复读)。
避免不可重复读(REPEATABLE READ)
操作步骤:
窗口A | 窗口B |
---|---|
set session transaction isolation level repeatable uncommitted; | start transaction; |
start transaction; |
|
| |
commit; | |
|
现象:事务内多次读取结果一致,但新增数据可能导致幻读。
3. 虚度(幻读 Phantom Read)
一个事务读取到了另一个事务提交的数据,导致了多次查询的结果不一致。强调是insert,向表中添加一条数据。
幻读
条件:REPEATABLE READ
操作步骤:
窗口A | 窗口B |
---|---|
start transaction; | start transaction; |
insert into account values (null,'杰克',1000); | |
select * from account; → 8条记录 | |
commit; | |
select * from account; → 仍为8条记录 commit; select * from account; → 新查询看到9条记录 |
现象:同一事务中两次查询结果集数量不同(MySQL通过MVCC部分解决幻读)。
避免各种读
操作步骤:
窗口A | 窗口B |
---|---|
set session transaction isolation level serializable; | |
start transaction; | start transaction; |
insert into account values (null,'小苍',10000); | |
select * from account; →显示插入成功,但实际上并未成功,数据库数据未发生改变 | |
commit; | |
select * from account; →新查询到数据库中的数据并未改变 |
现象:完全隔离,但并发性能最低。
3.4 事物的隔离级别
1. 事务的隔离级别
设置数据库的隔离级别,根据级别的不同,可以解决上述的读的问题。
- Read uncommitted ‐‐ 什么都解决不了
- Read committed ‐‐ 避免脏读,但是不可重复读和虚读有可能产生
- Repeatable read ‐‐ 避免脏读和不可重复读,虚度有可能产生的
- Serializable ‐‐ 避免各种读
2. 四种隔离级别有安全性和效率
- 安全 Serializable > Repeatable read > Read committed > Read uncommitted
- 效率 Serializable < Repeatable read < Read committed < Read uncommitted
3. 数据库默认的隔离级别
MySQL的数据库,默认的隔离级别是Repeatable read,避免脏读和不可重复读。
4. 总结
隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
---|---|---|---|---|
Read uncommitted | ❌ | ❌ | ❌ | 最高 |
Read committed | ✅ | ❌ | ❌ | 高 |
Repeatable read | ✅ | ✅ | ❌ | 中 |
Serializable | ✅ | ✅ | ✅ | 最低 |