文章目录
事务
一、事务的基本介绍
1、概念
如果包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
2、操作
-
① 开启事务:
start transaction;
-
② 回滚:
rollback;
-
③ 提交:
commit;
3、操作实例
account.sql
-- 创建account表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT;
NAME VARCHAR(10),
balance DOUBLE
);
-- 添加数据
INSERT INTO account (NAME,balance) VALUES ('zhangsan',1000),('lisi',1000);
SELECT * FROM account;
UPDATE account SET balance =1000;
--张三给李四转账500元
--0.开启事务
START TRANSACTION;
--1.张三账户-500
UPDATE account SET balance = balance - 500 WHERE NAME ='zhangsan';
--2.李四账户+500
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';
--发现执行没有问题,提交事务
COMMIT;
--发现出现问题,回滚事务
ROLLBACK;
4、MySQL数据库中默认自动提交
事务提交的两种方式
1)自动提交
- mysql是自动提交的
- 一条DML(增删改)语句会自动提交一次事务
2)手动提交
- Oracle 数据库默认是手动提交事务
- 需要先开启事务,然后手动提交
修改事务的默认提价方式
-
查看事务的默认提交方式:
SElECT @@autocommit;
--1 代表自动提交 0 代表手动提交 -
修改默认提交方式:
set @@autocommit = 0;
二、事务的四大特征
-
原子性(atomicity)
:事务必须是原子工作单元;对其数据修改,要么全都执行,要么全都不执行【最小的工作单位】 -
一致性(consistency
:事务在完成时,必须使所有的数据都保持一致状态【同时成功或者同时失败】 -
隔离性(isolation)
:由并发事务所作的修改必须与任何其他并发事务所作的修改隔离【事务与事务之间相互不影响】 -
持久性(durability)
:事务完成之后,它对于系统的影响是永久性的【事务一旦提交不可回滚】
三、事务的隔离级别
1、概念
多个事务之间隔离,相互独立的,但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题:
2、存在的问题
-
① 脏读:一个事务读取到另一个书屋中没有提交的数据
-
② 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样
-
③ 幻读:一个事务操作(DML)数据库中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
3、隔离级别
隔离的级别:
- ① read uncommitted: 读未提交
- 产生的问题:脏读,不可重复读,幻读
- ② read committed: 读已提交
- 产生的问题:不可重复读、幻读
- ③ repeatable read:可重复读
- 产生的问题:幻读
- ④ serializable:串行化
- 可以解决所有的问题
注意:隔离级别从小到大安全性越来越高,但是效率越来越低
隔离级别的设置
-
数据库查询隔离级别:
select @@tx_isolation;
-
数据库设置隔离级别:`set global transaction isolation level 级别字符串;
注:设置完隔离级别后需要重新打开mysql数据库才会生效。
演示
set global transaction isolation level read uncommitted;
start transaction;
-- 转账操作
update account set balance = balance - 500 where id = 1;
update account set balance = balance + 500 where id = 2;
四、Jdbc事务控制
1、概述
- 默认事务提交策略:一条命令自成一个完整事务
- 需求:各个逻辑单元要么一起成功,要么一起失败(比如转账)
2、控制事务的API:
使用Connection对象来管理事务
conn.setAutoCommit(false);
将jdbc的事务计较改为手动提交conn.commit();
手动提交conn.rollback();
如果事务出错,就让他它回滚
注意:连接提交策略一经设置,永久改变。
3、核心代码
将JDBC自动提交关闭,改成手动提交,然后在让出错的事务在异常里捕获成回滚。这样保证了提交事务出错数据不改动。从而达到数据的正确性。
步骤:
1. 获取连接Connection对象
2.Connection对象开启事务
3.Connection对象获取执行sql语句的Statement对象
4.定义2条sql语句(2条update语句:扣款,收款)
5.Statement对象执行sql语句,获取结果
6.如果sql语句正常执行,没有出现问题,提交事务
7.处理结果
8.如果sql语句执行过程中出现问题,回滚事务
9.关闭资源
代码:
public class Jdbc_affairDemo8 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
//1. 获取连接
try {
conn = JDBCUtils.getConnection();
// 建立连接后开启事务
conn.setAutoCommit(false);
//2. 定义sql
//2.1 张三 - 500
String sql1 = "update account set balance = balance - ? where id = ?";
//2.2 李四 + 500
String sql2 = "update account set balance = balance + ? where id = ?";
//3. 获取执行的sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4. 设置参数
pstmt1.setDouble(1, 500);
pstmt1.setInt(2, 1);
pstmt2.setDouble(1, 500);
pstmt2.setInt(2, 2);
//5. 执行sql
pstmt1.executeUpdate();
// 手动制造异常
int i = 3 / 0;
pstmt2.executeUpdate();
// 提交事务
conn.commit();
} catch (SQLException e) {
// 事务回滚
if (conn != null) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
JDBCUtils.close(pstmt1, conn);
JDBCUtils.close(pstmt2, null);
}
}
}