说明
在数据库事务中,有一个经典场景,银行转账,如下:
假设 马云 给 李雷 转账 1亿 人民币,想象一下,转账业务的操作流程:
- 从马云账户减少1亿
- 在李雷的账户上增加1亿
当 第1步 执行成功后,突然出现问题,导致 第2步 执行失败,此时会 发生巨大问题:钱丢了
代码演示
建表语句
CREATE TABLE `account` (
`aid` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(64) NOT NULL,
`money` DECIMAL(10,2) NOT NULL DEFAULT '0',
PRIMARY KEY (`aid`)
)
COLLATE='utf8mb4_unicode_ci'
;
初始化数据
INSERT INTO `account` (`aid`, `name`, `money`) VALUES (1, '马云', 10000.00);
INSERT INTO `account` (`aid`, `name`, `money`) VALUES (2, '李雷', 10.00);
java代码
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://127.0.0.1:3308/scott?useSSL=false&useUnicode=true&characterEncoding=utf-8";
Connection conn = DriverManager.getConnection(url,
"root",//用户名
"");//密码
//从马云账户减掉100
String sql="update account set money=money-? where aid=?";
PreparedStatement pstm=conn.prepareStatement(sql);
pstm.setInt(1, 100);
pstm.setInt(2,1);
pstm.executeUpdate();
pstm.close();
//======关键,演示报错代码==============
/*
* 上面代码,成功将马云账户减掉100元
* 运行该行代码时会报错,不再执行下面代码,也就不会将李雷账户增加100元
* 也就造成丢失100元的情况
*/
int i=1/0;
//给李雷账户加上100
String sql2="update account set money=money+? where aid=?";
PreparedStatement pstm2=conn.prepareStatement(sql2);
pstm2.setInt(1, 100);
pstm2.setInt(2,2);
pstm2.executeUpdate();
pstm2.close();
conn.close();
总结
类似下面情况:
- 从业务的角度讲:是 一个操作(可以理解为点一下按钮)
- 这一个操作,要对数据库进行 多次 增、删、改 操作(不是必须包含增、删、改3个操作)
那么 多次 增、删、改 都要成功,有一个失败,数据 就要 恢复到初始值,否则就会出现问题,即:数据不一致
类似案例
如:电商平台的下订单:
- 从业务角度讲:是 一个操作,即:下订单
- 下订单这个操作,要对数据库进行多次 增、删、改 操作,如下:
- 生成流水号,并将订单流水号、总金额、卖家信息、买家信息 保存到订单表中
- 将购买的多个商品的信息 保存到 订单商品表中
- 库存数要减少相应的数量
这些操作 要么都执行成功,有一个失败,数据就恢复到之前的数值
解决
事务 就是为了 避免 这种情况,详见 数据库事务的4个特性(ACID)