問題引入
面試的時候有時候會問到知不知道存儲過程,用沒用過?
是什么
存儲過程(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 存儲過程 | 菜鳥教程