数据库事务-银行转账问题 作者:马育民 • 2022-02-04 23:29 • 阅读:10129 # 说明 在数据库事务中,有一个经典场景,银行转账,如下: 假设 马云 给 李雷 转账 1亿 人民币,想象一下,转账业务的操作流程: 1. 从马云账户减少1亿 2. 在李雷的账户上增加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个操作) 那么 **多次 增、删、改 都要成功**,有一个失败,**数据 就要 恢复到初始值**,否则就会出现问题,即:数据不一致 ### 类似案例 如:电商平台的下订单: - 从业务角度讲:是 **一个操作**,即:下订单 - 下订单这个操作,要对数据库进行多次 **增、删、改** 操作,如下: 1. 生成流水号,并将订单流水号、总金额、卖家信息、买家信息 保存到订单表中 2. 将购买的多个商品的信息 保存到 订单商品表中 3. 库存数要减少相应的数量 这些操作 **要么都执行成功**,**有一个失败**,**数据就恢复到之前的数值** # 解决 **事务** 就是为了 **避免** 这种情况,详见 [数据库事务的4个特性(ACID)](https://www.malaoshi.top/show_1EF3uCskLf9s.html "数据库事务的4个特性(ACID)") 原文出处:http://malaoshi.top/show_1IX2idpROSDP.html