MySQL高级应用-2
- 1事务
- 应用场景:
- 以上三个场景的共同点是什么?
- 事务的概念:
- 事务特性ACID
- MySQL事务控制
- 实例演示
- 示例 1
- 示例 2
- 拓展:事务的隔离级别作用
- JAVA处理事务-模拟转账
- 2 自定义变量
- 用户变量
- 示例:
- 局部变量
- 示例
- 3 流程控制结构
- 分支结构
- IF分支结构
- CASE分支结构
- 循环结构
- 4 存储过程中的异常处理
- 5 存储过程**PROCEDURE**
- 含义
- 好处:
- 存储过程的缺陷:
- 创建语法
- 1) 过程名
- 2) 过程参数
- 3) 过程体
- 调用**语法:**
- 示例1:无参数的存储过程
- 示例2:有in和out参数的存储过程
- 示例3:MySQL存储过程+事务管理+异常处理实现模拟转账
- 示例4:存储过程返回SQL查询结果集-数据库各表统计分析
- 6. 在JAVA中通过JDBC和MyBatis中调用两个存储过程(转账、统计分析)
- 示例1:JDBC调用存储过程
- 示例2:MyBatis调用存储过程
1事务
应用场景:
1、注册账户(同时完成基本信息和详细信息):
基本的信息: 主键 登录用户名 密码
详细信息:主键 性别 年龄 爱好 收货地址(邮编 收货人 姓名 联系方式) 外键;
要求用户注册一定是两张表中都有相关记录,才认为注册成功。
2、订单: 订单信息表 订单的详情表
一个订单记录对应的是多个订单详情:
操作的表应该是两张,只有两张表中的记录都插入成功,认为这个订单操作才成功.
3.网吧管理:
会员 计算机 会员上机记录表
业务场景: 上机 : 记录表要增加一条记录,计算机表的计算机状态要改变,会员的状态要改变; 实际上三张表要发生变化,全部成功,上机才成功.
下机: 记录表要增加(修改)一条记录; 计算机表的计算机状态要改变; 会员的状态要改变; 会员的余额要改变; 实际上三张表要发生变化,全部成功,下机才成功.
以上三个场景的共同点是什么?
一个业务有多个sql语句的执行(insert update delete),多个sql全部执行成功,这个业务才有效;如果其中一条sql执行失败,整个业务都是失败的;
事务的概念:
数据库的事务(Transaction)是一种机制、一个操作序列,包含了一组数据库操作命令(CRUD操作)。事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行,因此事务是一个不可分割的工作逻辑单元。
在数据库系统上执行并发操作时,事务是作为最小的控制单元来使用的,特别适用于多用户同时操作的数据库系统。
例如,航空公司的订票系统、银行、保险公司以及证券交易系统等。
事务特性ACID
事务具有 4 个特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),这 4 个特性通常简称为 ACID。
1、原子性(Atomicity)
**事务是一个完整的操作。**事务的各元素是不可分的(原子的)。事务中的所有元素必须作为一个整体提交或回滚。如果事务中的任何元素失败,则整个事务将失败。
以银行转账事务为例,如果该事务提交了,则这两个账户的数据将会更新。如果由于某种原因,事务在成功更新这两个账户之前终止了,则不会更新这两个账户的余额,并且会撤销对任何账户余额的修改,事务不能部分提交。
2、一致性(Consistency)
当事务完成时,数据必须处于一致状态。也就是说,在事务开始之前,数据库中存储的数据处于一致状态。在正在进行的事务中. 数据可能处于不一致的状态,如数据可能有部分被修改。然而,当事务成功完成时,数据必须再次回到已知的一致状态。通过事务对数据所做的修改不能损坏数据,或者说事务不能使数据存储处于不稳定的状态。
以银行转账事务事务为例。在事务开始之前,所有账户余额的总额处于一致状态。在事务进行的过程中,一个账户余额减少了,而另一个账户余额尚未修改。因此,所有账户余额的总额处于不一致状态。
事务完成以后,账户余额的总额再次恢复到一致状态。
3、隔离性(Isolation)
对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。修改数据的事务可以在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。
另外,当事务修改数据时,如果任何其他进程正在同时使用相同的数据,则直到该事务成功提交之后,对数据的修改才能生效。张三和李四之间的转账与王五和赵二之间的转账,永远是相互独立的。
4、持久性(Durability)
事务的持久性指不管系统是否发生了故障,事务处理的结果都是永久的。
一个事务成功完成之后,它对数据库所作的改变是永久性的,即使系统出现故障也是如此。也就是说,一旦事务被提交,事务对数据所做的任何变动都会被永久地保留在数据库中。
事务的 ACID 原则保证了一个事务或者成功提交,或者失败回滚,二者必居其一。因此,它对事务的修改具有可恢复性。即当事务失败时,它对数据的修改都会恢复到该事务执行前的状态。
数据的角度:mysql、oracle都是支持事务操作。
MySQL事务控制
在mysql中,事务只针对innodb存储引擎,而MYISAM是非事务的存储引擎,不支持事务
一般来说,mysql默认开启了事务自动提交功能,每条sql执行都会提交事务。可以使用如下语句关闭事务自动提交功能。
1、 事务开始
begin或start transaction;
2、 事务提交
commit或commit work;
3、 回滚
rollback或rollback work;
4、 保存点设置
savepoint 标识;
5、 回滚到保存点
rollback to 标识;
6、 删除保存点
release savepoint 标识。
实例演示
下面通过两个例子来演示一下 MySQL 事务的具体用法。
示例 1
下面模拟在张三的账户减去 500 元后,账号编号李四的账户还未增加 500 时,有其他会话访问数据表的场景。由于代码需要在两个窗口中执行,为了方便阅读,这里我们称为 A 窗口和 B 窗口。1) 在 A 窗口中开启一个事务,更新 account 表”张三”的余额数据,SQL 语句和运行结果如下:
2) 在 B 窗口中查询 account 数据表中的数据,SQL 语句和运行结果如下:
从结果可以看出,虽然 A 窗口中的事务已经更改了 account表中的数据,但没有立即更新数据(没有提交事务),这时其他会话读取到的仍然是更新前的数据。
3) 在 A 窗口中继续执行事务,更新“李四”数据,并提交事务,SQL 语句和运行结果如下:
4) 在 B 窗口中再次查询 bank 数据表的数据,SQL 语句和运行结果如下:
在 A 窗口中执行 COMMIT 提交事务后,对数据所做的更新将一起提交,其他会话读取到的是更新后的数据。从结果可以看出张三和李四的总账户余额和转账前保持一致,这样数据从一个一致性状态更新到另一个一致性状态。
前面提到,当事务在执行中出现问题,也就是不能按正常的流程执行一个完整的事务时,可以使用** ROLLBACK** 语句进行回滚,使用数据恢复到初始状态。
在例 1 中,张三的账户余额已经减少到 500 元,如果再转出 1000 元,将会出现余额为负数,因此需要回滚到原始状态。如例 2 所示。
示例 2
将张三的账户余额减少 1000 元,并让事务回滚,SQL 语句和运行结果如下所示:
进行事务的回滚事务,然后再一次查询:
从结果可以看出,执行事务回滚后,账户数据恢复到初始状态,即该事务执行之前的状态。
拓展:事务的隔离级别作用
在数据库操作中,为了有效保证并发读取数据的正确性,提出了事务的隔离级别。在例 1 和例 2 的演示中,事务的隔离级别为默认隔离级别。
在 MySQL 中,事务的默认隔离级别是 REPEATABLE-READ (可重读)隔离级别,即事务未结束时(未执行 COMMIT 或 ROLLBACK),其它会话只能读取到未提交数据。
隔离性:一个事务的执行,不应该受到其他事务的干扰。
事务的隔离级别,如果不考虑隔离性,引发一些安全问题
①脏读:一个事务读到了另一个事务未提交的数据,导致查询结果不一致
②不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致多次查询结果不一致。
③虚读/幻读:一个事务读到了另一个事务已经提交的insert的数据,导致多次查询结果不一致。
设置事务的隔离级别:
read uncommitted (读取未提交内容) :脏读,不可重复读,虚读都有可能发生read committed (读取提交内容) :避免脏读。但是不可重复读和虚读是有可能发生repeatable read (可重读,默认) :避免脏读和不可重复读,但是虚读有可能发生。serializable(可串行化) :避免脏读,不可重复读,虚读。
JAVA处理事务-模拟转账
Java关键代码段
conn.setAutoCommit(false); //设置不默认提交
执行sql +
执行sql-
…
conn.commit();//提交事务
conn.rollback(); //回滚事务
以上的三个conn是同一个连接对象:
需求:张三向李四转账500,并且生成一条流水记录
2 自定义变量
说明: 变量是用户自定义的,不是由系统提供
使用步骤: ①声明②赋值③使用
根据作用范围分为用户变量,局部变量
用户变量
作用域: 针对于当前会话(连接)有效,同于会话变量的作用域
应用在任何地方,可以放在 BEGIN END 里面或外面
①声明并初始化
SET @``用户变量名
= ``值
;
SET@``用户变量名:
= ``值
;
②赋值(更新用户变量值)
方式一:通过 SET 或 SELECT
SET @``用户变量名
= ``值
;
SET @``用户变量名:
= ``值
;
SELECT @``用户变量名:
= ``值
;
例:
SET
@name=‘abc’;
SET
@name=1;
方式二:通过 SELECT INTO
SELECT``字段
INTO@``用户变量名
FROM ``表
;
例:
SET
@count=1;
SELECT
COUNT(*)
INTO
@count
FROM employees
;
SELECT
@count;
③查询
SELECT @``变量名
;
案例 声明2个用户变量并赋初值,求和并打印
SET
@num1
=
1;
SET
@num2
=
2;
SET
@sum
=
@num1
+
@num2;
SELECT
@sum;
//3
示例:
局部变量
作用域: 仅在定义它的 BEGIN END 中有效
应用在 BEGIN END 中的第一句话
使用:
①声明
DECLARE ``变量名 类型
;
DECLARE``变量名 类型
DEFAULT ``值
;
②赋值
方式一:通过 SET 或 SELECT
SET ``局部变量名
= ``值
;
SET``局部变量名:
= ``值
;
SELECT局部变量名:
= ``值
;
方式二:通过 SELECT INTO
SELECT``字段
INTO``局部变量名
FROM ``表
;
③查询
SELECT ``局部变量名
;
示例
类型 | 作用域 | 定义和使用的位置 | 语法 ** ** |
---|---|---|---|
用户变量 | 当前会话 | 会话中的任何地方 | 必须加@符号,不用限定类型 |
局部变量 | BEGIN END中 | 只能在 BEGIN END中,且为第一句 | 一般不加@符号,需要限定类型 |
3 流程控制结构
顺序结构:程序从上往下依次执行
分支结构:程序从多条路径中选择一条执行
循环结构:程序在满足一定条件的基础上,重复执行一段代码
分支结构
IF分支结构
IF 语句用来进行条件判断,根据是否满足条件(可包含多个条件),来执行不同的语句,是流程控制中最常用的判断语句。
功能: 实现多重分支
限制: 只能用在 BEGIN END 中
语法:
IF ``条件
1
THEN ``语句
1;
ELSEIF ``条件
2
THEN ``语句
2;…
ELSE ``语句n
;
END
IF;
案例 根据传入的成绩,返回等级
CASE分支结构
可以作为表达式,嵌套在其他语句使用,可以放在任何地方
可以作为独立语句使用,只能放在 BEGIN END 中,END 后要加 CASE
如果when中的值或条件成立,执行对应then后面的语句,结束 CASE,都不满足执行 ELSE 中的语句
ELSE 可以省略,如果都不满足返回 NULL
作为表达式:嵌套在其他语句使用,可以放在任何地方
情况1:类似switch ,实现等值判断
语法:
CASE ``变量名
|表达式
|字段
WHEN ``值
1
THEN ``要显示值``1``或者执行语句``1
WHEN``值n
THEN ``要显示值``n``或者执行语句``n
ELSE ``要显示值``m``或者执行语句``m
END;
情况2:类似多重if,实现区间判断
语法:
CASE
WHEN ``条件
1
THEN ``要显示值或者执行语句
WHEN``条件n
THEN ``要显示值或者执行语句
ELSE ``要显示值或者执行语句
END;
作为独立语句时:
只能放在 BEGIN END 中,END 后要加 CASE
情况1:类似switch ,实现等值判断
CASE ``变量名
|表达式
|字段
WHEN ``值
1
THEN ``语句
1;…
WHEN``值n
THEN ``语句值n
;
ELSE ``语句m
;
END
CASE;
情况2:类似多重if,实现区间判断
语法:
CASE
WHEN ``条件
1
THEN ``语句
1;…
WHEN``条件n
THEN ``语句n
;
ELSE ``语句m
;
END
CASE;
循环结构
分类:
WHILE
LOOP
REPEAT**循环控制:**ITERATE 类似于continue 结束本次循环,继续下一次LEAVE 类似于break,结束当前所在循环
示例地址:https://www.cnblogs.com/crazytata/p/10031860.html
1 WHILE语法:
WHILE``循环条件
DO
循环体;
END
WHILE;
示例:
2 LOOP语法:
LOOP
循环体;
END
LOOP
示例:
3 REPEAT
语法:
REPEAT
循环体;
UNTIL 结束循环的条件
END
REPEAT
;
示例:
4 存储过程中的异常处理
具体请查看:https://www.cnblogs.com/geaozhang/p/6814567.html
Q:何为异常?
A:程序在执行过程中有可能出错,运行时错误叫做异常。
默认情况下,当存储过程运行出错时,过程会立即终止,并打印系统错误消息。
5 存储过程PROCEDURE
含义
我们前面所学习的 MySQL 语句都是针对一个表或几个表的单条 SQL 语句,但是在数据库的实际操作中,经常会有需要多条 SQL 语句处理多个表才能完成的操作。
例如,为了确认学生能否毕业,需要同时查询学生档案表、成绩表和综合表,此时就需要使用多条 SQL 语句来针对这几个数据表完成处理要求。
存储过程是一组为了完成特定功能的 SQL 语句集合。使用存储过程的目的是将常用或复杂的工作预先用 SQL 语句写好并用一个指定名称存储起来,这个过程经编译和优化后存储在数据库服务器中,因此称为存储过程。当以后需要数据库提供与已定义好的存储过程的功能相同的服务时,只需调用“CALL存储过程名字”即可自动完成。
对于调用者来说,存储过程封装了 SQL 语句,调用者无需考虑逻辑功能的具体实现过程。只是简单调用即可,它可以被 Java 和 C# 等编程语言调用。
好处:
提高代码重用性、简化操作、减少编译次数,减少和数据库连接次数
存储过程的缺陷:
l 不同DBMS中的存储过程语法有所不同,编写真正的可移植存储过程几乎是不可能的,不过存储过程的自我调用可以相对保持可移植
l 编写存储过程比编写基本SQL语句复杂,需要更高的技能,更丰富的经验
创建语法
可以使用 CREATE PROCEDURE 语句创建存储过程。
语法格式如下:
CREATE PROCEDURE 存储过程名(过程参数列表)
BEGIN
sql语句…
END
过程参数列表包含三部分: 参数模式 参数名 参数类型
过程参数列表格式:[ IN | OUT | INOUT ] <参数名> <类型>例: IN V_NAME VARCHAR
1) 过程名
存储过程的名称,默认在当前数据库中创建。若需要在特定数据库中创建存储过程,则要在名称前面加上数据库的名称,即 db_name.sp_name。需要注意的是,名称应当尽量避免选取与 MySQL 内置函数相同的名称,否则会发生错误。
2) 过程参数
存储过程的参数列表。其中,<``参数名>
为参数名,<``类型>
为参数的类型(可以是任何有效的 MySQL 数据类型)。当有多个参数时,参数列表中彼此间用逗号分隔。存储过程可以没有参数(此时存储过程的名称后仍需加上一对括号),也可以有 1 个或多个参数。MySQL 存储过程支持三种类型的参数,即输入参数、输出参数和输入/输出参数,分别用 IN、OUT 和 INOUT 三个关键字标识。其中,输入参数可以传递给一个存储过程,输出参数用于存储过程需要返回一个操作结果的情形,而输入/输出参数既可以充当输入参数也可以充当输出参数。
需要注意的是,参数的取名不要与数据表的列名相同,否则尽管不会返回出错信息,但是存储过程的 SQL 语句会将参数名看作列名,从而引发不可预知的结果。
3) 过程体
存储过程的主体部分,也称为存储过程体,包含在过程调用的时候必须执行的 SQL 语句。这个部分以关键字 BEGIN 开始,以关键字 END 结束。
调用语法:
CALL ``存储过程名
(实参列表
);
示例1:无参数的存储过程
需求:对test表插入10条记录
示例2:有in和out参数的存储过程
需求:
– 根据账户名,返回账户余额和账户等级,等级规则
– 余额在>0之间"黑铁"
– 余额在>1000之间"青铜"
– 余额在>2000之间"白银"
– 余额在>3000之间"黄金"
– 余额在>5000以上"铂金"
示例3:MySQL存储过程+事务管理+异常处理实现模拟转账
模拟转账: +钱 -钱 生成流水记录 张三–>李四转账500 张三-500 李四+500 添加一条流水记录
转出者余额不足,返回"余额不足,转账不足"
SQL出现异常,事务回滚,并且返回"系统异常,转账失败"
SQL正常执行,事务提交,并且返回"转账成功"
测试
示例4:存储过程返回SQL查询结果集-数据库各表统计分析
测试
6. 在JAVA中通过JDBC和MyBatis中调用两个存储过程(转账、统计分析)
示例1:JDBC调用存储过程
示例2:MyBatis调用存储过程
定义接口:
定义接口的实现映射文件
测试