Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能。
欢迎大家加我微信itsoku一起交流java、算法、数据库相关技术。
这是Mysql系列第20篇。
环境:mysql5.7.25,cmd命令中进行演示。
代码中被[]包含的表示可选,|符号分开的表示可选其一。
需求背景
我们在写存储过程的时候,可能会出现下列一些情况:
插入的数据违反唯一约束,导致插入失败
插入或者更新数据超过字段最大长度,导致操作失败
update影响行数和期望结果不一致
遇到上面各种异常情况的时,可能需要我们能够捕获,然后可能需要回滚当前事务。
本文主要围绕异常处理这块做详细的介绍。
此时我们需要使用游标,通过游标的方式来遍历select查询的结果集,然后对每行数据进行处理。
本篇内容
异常分类详解
内部异常详解
外部异常详解
掌握乐观锁解决并发修改数据出错的问题
update影响行数和期望结果不一致时的处理
准备数据
创建库:javacode2018
创建表:test1,test1表中的a字段为主键。
-
/*建库javacode2018*/
-
drop database
if exists javacode2018;
-
create database javacode2018;
-
/*切换到javacode2018库*/
-
use javacode2018;
-
DROP
TABLE
IF
EXISTS test1;
-
CREATE
TABLE
test1(a int
PRIMARY
KEY);
异常分类
我们将异常分为mysql内部异常和外部异常
mysql内部异常
当我们执行一些sql的时候,可能违反了mysql的一些约束,导致mysql内部报错,如插入数据违反唯一约束,更新数据超时等,此时异常是由mysql内部抛出的,我们将这些由mysql抛出的异常统称为内部异常。
外部异常
当我们执行一个update的时候,可能我们期望影响1行,但是实际上影响的不是1行数据,这种情况:sql的执行结果和期望的结果不一致,这种情况也我们也把他作为外部异常处理,我们将sql执行结果和期望结果不一致的情况统称为外部异常。
Mysql内部异常
示例1
test1表中的a字段为主键,我们向test1表同时插入2条数据,并且放在一个事务中执行,最终要么都插入成功,要么都失败。
创建存储过程:
-
/*删除存储过程*/
-
DROP
PROCEDURE
IF
EXISTS proc1;
-
/*声明结束符为$*/
-
DELIMITER $
-
/*创建存储过程*/
-
CREATE
PROCEDURE
proc1(a1 int,a2 int)
-
BEGIN
-
START
TRANSACTION;
-
INSERT
INTO
test1(a)
VALUES (a1);
-
INSERT
INTO
test1(a)
VALUES (a2);
-
COMMIT;
-
END $
-
/*结束符置为;*/
-
DELIMITER ;
上面存储过程插入了两条数据,a的值都是1。
验证结果:
-
mysql>
DELETE
FROM test1;
-
Query
OK,
0 rows affected (
0.00 sec)
-
mysql>
CALL
proc1(
1,
1);
-
ERROR
1062 (
23000):
Duplicate entry
'1'
for key
'PRIMARY'
-
mysql>
SELECT *
from test1;
-
+---+
-
| a |
-
+---+
-
|
1 |
-
+---+
-
1 row
in set (
0.00 sec)
上面先删除了test1表中的数据,然后调用存储过程
proc1
,由于test1表中的a字段是主键,插入第二条数据时违反了a字段的主键约束,mysql内部抛出了异常,导致第二条数据插入失败,最终只有第一条数据插入成功了。上面的结果和我们期望的不一致,我们希望要么都插入成功,要么失败。
那我们怎么做呢?我们需要捕获上面的主键约束异常,然后发现有异常的时候执行rollback
回滚操作,改进上面的代码,看下面示例2。
示例2
我们对上面示例进行改进,捕获上面主键约束异常,然后进行回滚处理,如下:
创建存储过程:
-
/*删除存储过程*/
-
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
test1(a)
VALUES (a1);
-
INSERT
INTO
test1(a)