/*删除存储过程*/ drop procedure if exists proc2; /*声明结束符为$*/ delimiter $ /*创建存储过程*/ create procedure proc2(a1 int,a2 int) begin /*声明一个变量,用于标识是否有SQL异常*/ declare hasSqlError int default false; /*在执行过程中出现任何异常时,设置hasSqlError值为true*/ declare continue handler for sqlexception set hasSqlError=true; /*开启事务*/ start transaction; insert into book4(a) values(a1); insert into book4(a) values(a2); /*根据hasSqlError来判断是否有异常,如果有就回滚和提交*/ if hasSqlError then rollback; else commit; end if; end $ /*设置结束符为;$*/ delimiter ;
可以看到在上面那个存储过程中,有一条核心语句:
1
declare continue handler for sqlexception set hasSqlError=true;
/*1、查询余额,并判断余额是否足够*/ select a.money into v_money from t_account as a where a.user_id = v_user_id; if v_money <=v_price then set v_msg = '账户余额不足'; /*退出*/ leave a; end if;
/*模拟耗时5秒*/ select sleep(5);
/*2、余额减去price*/ set v_money = v_money - v_price; /*3、更新余额*/ start transaction; update t_account set money = v_money where user_id = v_user_id; /*4、插入订单明细*/ insert into t_order(price) values(v_price); /*5、提交事务*/ commit; set v_msg= '下单成功'; end $ /*设置结束符为;$*/ delimiter ;
可以看到这里用户下单其实分为3个步骤,(1)验证账户余额;(2)修改余额变量;(3)更新余额。
第三步,打开两个CMD窗口,连接MySQL,同时执行如下语句:
1 2 3
use envythink; call proc3(1001,100,@v_msg); select @v_msg;
之后查询两个表中的数据,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
mysql> select * from t_account; +---------+-----------+ | user_id | money | +---------+-----------+ | 1001 | 900.00 | +---------+-----------+ 1 row in set (0.00 sec)
mysql> select * from t_order; +----+--------+ | id | price | +----+--------+ | 1 | 100.00 | | 2 | 100.00 | +----+--------+ 2 rows in set (0.00 sec)
drop table if exists t_account; create table t_account( user_id int primary key comment '用户id', money decimal(10,2) not null default 0 comment '账户余额', version int default 0 comment '版本号,每次更新会加1' )comment '账户表';
drop table if exists t_order; create table t_order( order_id int primary key auto_increment comment '订单id', price decimal(10,2) not null default 0 comment '订单金额' )comment '订单表';
delete from t_account; insert into t_account(user_id,money) values(1001,1000);
/*删除存储过程*/ drop procedure if exists proc4; /*声明结束符为$*/ delimiter $ /*创建存储过程*/ create procedure proc4(v_user_id int,v_price decimal(10,2),out v_msg varchar(64)) a:begin /*保存当前余额*/ declare v_money decimal(10,2); /*保存版本号*/ declare v_version int default 0; /*保存影响的行数*/ declare v_update_count int default 0;
/*1、查询余额,并判断余额是否足够*/ select a.money into v_money from t_account as a where a.user_id = v_user_id; if v_money <=v_price then set v_msg = '账户余额不足'; /*退出*/ leave a; end if;
/*模拟耗时5秒*/ select sleep(5);
/*2、余额减去price*/ set v_money = v_money - v_price; /*3、更新余额*/ start transaction; update t_account set money = v_money where user_id = v_user_id; /*获取上面update影响的函数*/ select row_count() into v_update_count; /*4、插入订单明细*/ if v_update_count =1 then insert into t_order(price) values(v_price); set v_msg= '下单成功'; /*5、提交事务*/ commit; else set v_msg= '下单失败,请重试!'; /*6、回滚事务*/ rollback; end if; end $ /*设置结束符为;$*/ delimiter ;