问题引入
面试的时候有时候会问到知不知道存储过程,用没用过?
是什么
存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。
说白了就是一组sql语句集,中间可能还有一些逻辑操作,那么问题来了,反正就是一组sql语句集,我写多个mapper,service掉多个也能实现啊,为什么要用他?往下看
为什么
即有什么优势
- 重复利用。 个人理解为 相同逻辑下,另一个系统可以直接调用存储过程而不需要在写代码。因为一个系统我把存储过程的sql分开了,划分成功能更小的mapper更便于我后续开发,更便于我之后的重复利用。
- 减少网络流量 。 调用的时候只传送存储过程名和参数(参数值,参数进出类型,参数数据类型),减少了传送sql。
- 安全。 sql存储在存储过程中(数据库),可以防止sql注入
- 存储过程出问题之后,不需要重启项目。大的项目部署时间花费很长,如果是存储过程中出现了问题,只需要修改存储过程即可。
- 多个sql可以一次执行,减少链接池的连接 个人理解(感觉这才是用存储最大的优点,好多地方居然没写)
有优势肯定会有劣势,那么看一下有什么劣势
- 调试麻烦。在数据库连接工具里面其实都能看过哪一步有错,其实也算不上调试麻烦,只不过人家这样写教材,你就的这样回答。
- 维护困难。存储过程的语法和sql还有点不一样,据说某讯有一个800多行的存储过程,维护的时候看的人都傻了。
- 移植问题。
- 重新编译问题。 因为后端代码是运行前编译的,如果带有引用关系的对象发生改变时,受影响的存储过程、包将需要重新编译(不过也可以设置成运行时刻自动编译)。
什么情况下试用
依据他的优点
- 需要多次频繁的去和数据库连接,可以交给存储过程,减少和数据库的连接过程浪费的时间
- 由于其安全性,传统银行的项目,必不可少了
- sql很长 巨长那种
怎么用
以下会结合mybatis写一个简单的带参数的执行过程。
情景模拟: 有一个用户表,还有一个用户绩效表,为了方便绩效表里存了用户姓名,有一天用户名被修改了,①那么绩效表里的用户名也应该修改(其他关联地方都应该修改),②或者新加用户之后,绩效表里也要新添加用户的姓名和id
这里我们用②来做个简单的存储过程。
数据库中执行下列语句直接形成存储过程
DELIMITER $$
USE `cms`$$
CREATE DEFINER=`root`@`%` PROCEDURE `insert_user`(
IN user_name VARCHAR(45),
IN user_age int (11)
)
BEGIN
INSERT INTO `cms`.`demo` (`user_name`, `user_age`) VALUES (user_name, user_age);
select @@IDENTITY from `cms`.`demo`;
INSERT INTO `cms`.`demo1` (`id`) VALUES (@@IDENTITY);
END$$DELIMITER ;
DELIMITER $$
--DELIMITER是定界符 和最后的呼应形成一个完整的存储过程 $$也可以用//表示 将语句的结束符号从分号;临时改为两个$$
USE `cms`$$
--用cms这个库 $$ 上面已经说明这是个一句话说完的标识
CREATE DEFINER=`root`@`%` PROCEDURE `insert_user`(
--CREATE:创建的关键字 DEFINER=`root`@`%`编译自己给加的不知道什么东西 PROCEDURE:声明是个存储过程 `insert_user`:这个是存储过程的方法名 括号里面为参数
IN user_name VARCHAR(45),
--IN:输入还是输出的方式,user_name:参数名 VARCHAR(45):参数类型
IN user_age int (11)
)
BEGIN
--开始的标识
INSERT INTO `cms`.`demo` (`user_name`, `user_age`) VALUES (user_name, user_age);
--这个sql不用多解释了吧,就是传入的名字和年龄存储到demo
select @@IDENTITY from `cms`.`demo`;
--这句话的意思是获取刚才插入到demo表中数据的id
INSERT INTO `cms`.`demo1` (`id`) VALUES (@@IDENTITY);
--将刚才获取的id插入到demo1里
END$$ --结束的标识
DELIMITER ; --定界符
实际操作中
写完之后就是mapper.xml了
<insert id="addUser" statementType="CALLABLE">{call insert_user(#{demo.userName,mode=IN},#{demo.userAge,mode=IN,jdbcType=INTEGER})}</insert>
调用存储过程的方法用call 另外注意statementType="CALLABLE"标志着这个是执行存储过程。
然后参数传递的时候注明是入还是出和存储过程的方法参数对应上eg:mode=IN ,不是String的要标明类型eg:jdbcType=INTEGER
这仅仅是个简单的存储过程如果遇到复杂的有赋值,判断,循环等等的其他的复杂的逻辑,可以查阅其他资料学一下。
MySQL 存储过程 | 菜鸟教程