zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

MySQL数据库,详解异常捕获及处理(三)

2023-03-31 10:28:36 时间

示例2

对⽰例1进⾏优化。

创建表:

DROP TABLE IF EXISTS t_funds;

CREATE TABLE t_funds(

user_id INT PRIMARY KEY COMMENT '⽤户id',

available 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(

id int PRIMARY KEY AUTO_INCREMENT COMMENT '订单id',

price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '订单⾦额'

)COMMENT '订单表';

delete from t_funds;

/*插⼊⼀条数据,⽤户id为1001,余额为1000*/

INSERT INTO t_funds (user_id,available) 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_available DECIMAL(10,2);

/*保存版本号*/

DECLARE v_version INT DEFAULT 0;

/*保存影响的⾏数*/

DECLARE v_update_count INT DEFAULT 0;

/*1.查询余额,判断余额是否够*/

select a.available,a.version into v_available,v_version from

t_funds a where a.user_id = v_user_id;

if v_available<=v_price THEN

SET v_msg='账户余额不⾜!';

/*退出*/

LEAVE a;

END IF;

/*模拟耗时5秒*/

SELECT sleep(5);

/*2.余额减去price*/

SET v_available = v_available - v_price;

/*3.更新余额*/

START TRANSACTION;

UPDATE t_funds SET available = v_available WHERE user_id =

v_user_id AND version = v_version;

/*获取上⾯update影响⾏数*/

select ROW_COUNT() INTO v_update_count;

IF v_update_count=1 THEN

/*插⼊订单明细*/

INSERT INTO t_order (price) VALUES (v_price);

SET v_msg='下单成功!';

/*提交事务*/

COMMIT; ELSE

SET v_msg='下单失败,请重试!';

/*回滚事务*/

ROLLBACK;

END IF;

END $

/*结束符置为;*/

DELIMITER ;

ROW_COUNT()可以获取更新或插⼊后获取受影响⾏数。将受影响⾏数放在

v_update_count中。

然后根据v_update_count是否等于1判断更新是否成功,如果成功则记录订单

信息并提交事务,否则回滚事务。

验证结果:开启2个cmd窗⼝,连接mysql,执⾏下⾯操作:

use javacode2018;

CALL proc4(1001,100,@v_msg);

select @v_msg;

窗⼝1结果:

mysql> CALL proc4(1001,100,@v_msg);

+----------+

| sleep(5) |

+----------+

| 0 |

+----------+

1 row in set (5.00 sec)

Query OK, 0 rows affected (5.00 sec)

mysql> select @v_msg;

+---------------+

| @v_msg |

+---------------+

| 下单成功! |

+---------------+

1 row in set (0.00 sec)窗⼝2结果:

mysql> CALL proc4(1001,100,@v_msg);

+----------+

| sleep(5) |

+----------+

| 0 |

+----------+

1 row in set (5.00 sec)

Query OK, 0 rows affected (5.01 sec)

mysql> select @v_msg;

+-------------------------+

| @v_msg |

+-------------------------+

| 下单失败,请重试! |

+-------------------------+

1 row in set (0.00 sec)

可以看到第⼀个窗⼜下单成功了,窗⼜2下单失败了。

再看⼀下2个表的数据:

mysql> SELECT * FROM t_funds;

+---------+-----------+---------+

| user_id | available | version |

+---------+-----------+---------+

| 1001 | 900.00 | 0 |

+---------+-----------+---------+

1 row in set (0.00 sec)

mysql> SELECT * FROM t_order;

+----+--------+

| id | price |

+----+--------+

| 1 | 100.00 |

+----+--------+

1 row in set (0.00 sec)

也正常。总结

1. 异常分为Mysql内部异常和外部异常

2. 内部异常由mysql内部触发,外部异常是sql的执⾏结果和期望结果不⼀致导致的错误

3. sql内部异常捕获⽅式

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET hasSqlError=TRUE;

4. ROW_COUNT()可以获取mysql中insert或者update影响的⾏数

5. 掌握使⽤乐观锁(添加版本号)来解决并发修改数据可能出错的问题

6. begin end前⾯可以加标签,LEAVE 标签可以退出对应的begin end,可以使⽤这个

来实现return的效果