声明:本文主要以运用为主,不介绍相关概念。
目录
存储过程
函数
Java使用存储过程、函数(创建、运行)示例
欢迎来到JustryDeng的博客!正文start!
存储过程
创建存储过程:
CREATE
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
各参数说明:
[]括起来的:表示是可选的。
DEFINER:创建者,默认是CURRENT_USER。此功能与SQL SECURITY可保证使用存储过程的权限等安全性。
sp_name:存储过程的名称。
proc_parameter:参数,其具体格式为:[ IN | OUT | INOUT ] param_name type
注:IN表示输入参数,OUT表示输出参数,INOUT表示即可作为输入参数又可作为输出参数。
不指定的话,默认为IN。其中type指的是任何有效的MySQL数据类型。
注:可以将存储过程的计算结果保存在OUT/INOUT参数里面,变相把结果返回。
characteristic:当前存储过程的特性,可设置的特性有:
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
routine_body:具体存储过程逻辑,可以包含任何常规有效的SQL语句(即:可以包含SQL函数、增删改查等)。
注:如果含有多条语句,那么需要在前后分别加上BEGIN、END来标识开始与结束。
提示:BEGIN、END可以嵌套。
调用存储过程:
CALL 存储过程名(参数...);
如:
删除存储过程:
-- 如果存储过程存在,那么进行删除
DROP PROCEDURE IF EXISTS 存储过程名;
如:
存储过程的创建与使用示例(部分知识点,在示例中讲解):
if-else示例:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS ifElseProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE ifElseProcedure ( IN paramA INT, IN paramB INT, OUT paramC VARCHAR(40) )
BEGIN
IF paramA = 1 THEN set paramC = '参数paramA的值为1';
ELSEIF paramA = 2 THEN set paramC = '参数paramA的值为2' ;
ELSEIF paramA = paramB THEN set paramC = '参数paramA的值 = 参数paramB的值' ;
ELSE set paramC = '参数有误!' ;
END IF;
END//
DELIMITER说明:DELIMITER用于定义存储过程、函数等的结束符(默认结束符为“;”),当MySQL读取到结束符时,
就会认为命令已经读取完毕,可以执行了,但是在一个存储过程或者一个函数中,可能包含多
个“;”,如果mysql读取到第一个“;”时就执行,那么就可能导致执行失败(因为mysql还没有读取
到完整的命令)。使用DELIMITER就可以解决这个问题。使用方式如:DELIMITER //,那么结
束符就变成了“//”,那么当存储过程中出现“//”时,就代表存储过程结束了(具体使用方式如上
所示)。
追注:此时常规SQL语句的的结束符仍然是“;”。
set赋值说明:对于OUT、INOUT参数的赋值,使用set 参数A = 参数值a, 参数B = 参数值b的方式进行赋值。
追注:在存储过程中对IN类别的参数进行赋值,该赋值操作只在该存储过程内部有效;调用存
储过程后,如果在外部观察该变量的值,会发现和原来是一样的,并没有受到存储过程
中的赋值操作的影响;但是在存储过程内部来观察,会发现该值确实发生了变化。此处
类似于java中的实参与形参的部分区别,其实是一种域的体现;这也解释能理解为什么
会有IN、OUT、INOUT三种参数类别了。
into赋值说明:在某些情况下,我们也可以使用INTO来进行赋值,如:
使用测试:
case when示例:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS caseWhenProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE caseWhenProcedure ( IN paramA INT, INOUT paramB VARCHAR(20) )
BEGIN
DECLARE a,b,c VARCHAR(5) DEFAULT 'none';
CASE paramA
WHEN 80 THEN SET a = 'HTTP';
WHEN 443 THEN SET b = 'HTTPS';
WHEN 21 THEN SET c = 'FTP';
ELSE SET a = '0', b = '0', c = '0';
END CASE;
set paramB = CONCAT(a, ',', b, ',', c);
END//
上述SQL等价于:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS caseWhenProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE caseWhenProcedure ( IN paramA INT, INOUT paramB VARCHAR(20) )
BEGIN
DECLARE a,b,c VARCHAR(5) DEFAULT 'none';
CASE
WHEN paramA =80 THEN SET a = 'HTTP';
WHEN paramA =443 THEN SET b = 'HTTPS';
WHEN paramA =21 THEN SET c = 'FTP';
ELSE SET a = '0', b = '0', c = '0';
END CASE;
set paramB = CONCAT(a, ',', b, ',', c);
END//
局部变量说明:局部变量的声明方式为:
DECLARE var_name [, var_name] ... type [DEFAULT value]
注:局部变量的赋值方式与参数一样,使用set进行赋值或者使用into进行赋值。
注:局部变量只能在BEGIN、END之间进行定义,END结束后,局部变量失效。
注:对于字符串的拼接可以使用CONCAT函数来完成。
使用测试:
loop示例:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS loopProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE loopProcedure ( IN paramA INT, INOUT paramB VARCHAR(100) )
BEGIN
DECLARE i INT DEFAULT 0;
my_label: LOOP
-- 循环体
SET paramB = CONCAT(paramB, i);
-- 变量递增(注:此处暂不支持i++、++i的形式)
SET i = i + 1;
-- 临界条件,判断是否结束循环
IF i > paramA THEN
LEAVE my_label;
END IF;
END LOOP;
END//
注:此处的LEAVE xxx;跳出指定循环体的概念与Java中的break xxx;跳出指定循环体的概念其实是一样的。
使用测试:
repeat示例:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS repeatProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE repeatProcedure ( IN paramA INT, INOUT paramB VARCHAR(100) )
BEGIN
DECLARE i INT DEFAULT 0;
REPEAT
-- 循环体
SET paramB = CONCAT(paramB, i);
-- 变量递增(注:此处暂不支持i++、++i的形式)
SET i = i + 1;
-- 临界条件,判断是否结束循环
UNTIL i > paramA END REPEAT;
END//
注:repeat与loop都是至少先执行一次,然后再判断是否继续循环。
使用测试:
while示例:
-- 如果存储过程已经存在,那么移除
DROP PROCEDURE IF EXISTS whileProcedure;
-- 设置存储过程以“//”为结束符
DELIMITER //
CREATE PROCEDURE whileProcedure ( IN paramA INT, INOUT paramB VARCHAR(100) )
BEGIN
DECLARE i INT DEFAULT 0;
-- 临界条件,判断是否结束循环
WHILE i <= 3 DO
-- 循环体
SET paramB = CONCAT(paramB, i);
-- 变量递增(注:此处暂不支持i++、++i的形式)
SET i = i + 1;
END WHILE;
END//
注:while是先判断,再决定是否继续循环,而repeat和loop都是至少先执行一次,然后再判断是否继续循环。
使用测试:
函数
创建函数
CREATE
[DEFINER = { user | CURRENT_USER }]
FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
各参数说明:
[]括起来的:表示是可选的。
DEFINER:创建者,默认是CURRENT_USER。此功能与SQL SECURITY可保证使用存储过程的权限等安全性。
sp_name:函数的名称。
func_parameter:参数,其具体格式为:param_name type
注:此处函数与存储过程不同,函数的参数没有IN/OUT/INOUT之分(其实相当于只有输入参数)。
type:返回值的数据类型(任何有效的MySQL数据类型)。注:函数的返回值只有一个。
characteristic:当前存储过程的特性,可设置的特性有:
COMMENT 'string'
| LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
routine_body:具体函数逻辑,可以包含任何常规有效的SQL语句(即:可以包含SQL函数、增删改查等)。
注:如果含有多条语句,那么需要在前后分别加上BEGIN、END来标识开始与结束。
提示:BEGIN、END可以嵌套。
调用函数:
SELECT 函数名(参数...);
如:
删除函数:
-- 如果函数存在,那么进行删除
DROP FUNCTION IF EXISTS 函数名;
如:
函数的创建与使用示例:
说明:函数与存储过程比较相似,都是对一些SQL逻辑的封装,熟悉掌握了存储过程,就能很快上手函数。
下面简单实例一下函数的创建与使用:
创建示例:
-- 如果函数已经存在,那么移除
DROP FUNCTION IF EXISTS myFunction;
-- 设置函数以“//”为结束符
DELIMITER //
CREATE FUNCTION myFunction(paramA INT, paramB INT)
-- 返回值类型
RETURNS VARCHAR(50)
-- 如果函数体有多条语句,那么函数体需要放在BEGIN 与 END中
BEGIN
DECLARE diffValue INT;
DECLARE returnValue varchar(20) DEFAULT '';
set diffValue = paramA - paramB;
set returnValue = CONCAT(paramA,' - ', paramB, ' = ', diffValue);
-- 返回结果
RETURN returnValue;
END//
使用示例:
Java使用存储过程、函数(创建、运行)示例:
P.S.作为一个程序员,我不想说太多话,直接上代码吧。
Java创建并使用存储过程示例:
涉及到的mapper中的接口有:
import com.aspire.model.ProcedureModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.StatementType;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* mapper注解
*
* 说明:灵活运用@Select等注解即可
*
*
* @author JustryDeng
* @date 2019/1/25 21:10
*/
@Mapper
public interface JustryDengMapper {
/**
* 查询 存储过程/函数 是否存在
*
* 注:mysql.proc记录的是所有数据库中的存储过程名,查询时最好指定数据库名;
* 当然如果能保证所有数据库中的存储过程的唯一性的话,也可以不指定数据库名
*
* @param name
* 存储过程/函数 的 名字
* @param database
* 数据库
* @param type
* 类型,其值只能为 PROCEDURE 或者 FUNCTION
*
* @return 是否存在
* @date 2019/1/27 10:16
*/
@Select("SELECT IF (COUNT(*)=0,FALSE,TRUE) FROM mysql.proc WHERE db=#{database} "
+ " AND `type`=#{type} AND `name`=#{name}")
boolean procedureOrFunctionIsExist (@Param("name") String name,
@Param("database") String database,
@Param("type") String type);
/**
* 创建 存储过程/函数 的SQL语句
*
* @param procedureOrFunction
* 存储过程/函数 的 创建语句
*
* @date 2019/1/25 21:17
*/
@Select("${procedureOrFunction}")
void createProcedureOrFunction (@Param("procedureOrFunction") String procedureOrFunction);
/**
* 调用存储过程(以map作为参数容器)
*
* 注:存储过程是将结果,放在OUT/INOUT参数里面,所以这里的返回值用void即可;
* 就算是用Object接收参数,接受到的也是null
*
* 注:就算传入map中原本就没有OUT对应的key(这里即为keyThree),那么执行存
* 储过程后,也会也会自动put进去
*
* @param paramsMap
* map容器,容纳 IN 、 OUT 、 INOUT参数
*
* @date 2019/1/27 10:35
*/
@Select("CALL ifElseProcedure ("
+ " #{map.keyOne, mode = IN, jdbcType = NUMERIC}, "
+ " #{map.keyTwo, mode = IN, jdbcType = NUMERIC},"
+ " #{map.keyThree, mode = OUT, jdbcType = VARCHAR}"
+ ")")
@Options(statementType = StatementType.CALLABLE)
void callProcedureByMap (@Param("map") Map<String, Object> paramsMap);
/**
* 调用存储过程(以对象作为参数容器)
*
* 注:存储过程是将结果,放在OUT/INOUT参数里面,所以这里的返回值用void即可;
* 就算是用Object接收参数,接受到的也是null
*
* 注:模型必须有相应的字段,且必须有setter、getter方法
*
* @param pm
* 对象容器,容纳 IN 、 OUT 、 INOUT参数
*
* @date 2019/1/27 10:35
*/
@Select("CALL ifElseProcedure ("
+ " #{paramA, mode = IN, jdbcType = NUMERIC}, "
+ " #{paramB, mode = IN, jdbcType = NUMERIC},"
+ " #{paramC, mode = OUT, jdbcType = VARCHAR}"
+ ")")
@Options(statementType = StatementType.CALLABLE)
void callProcedureByModel (ProcedureModel pm);
}
测试方法为:
import com.aspire.mapper.JustryDengMapper;
import com.aspire.model.ProcedureModel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.Map;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MysqlFunctionProcessApplicationTests {
@Autowired
JustryDengMapper mapper;
/**
* java程序使用MySQL存储过程示例
*
* @author JustryDeng
* @date 2019/1/27 10:24
*/
@Test
public void useProcedureDemo() {
final String database = "demo";
final String procedureType = "PROCEDURE";
// -> 判断存储过程ifElseProcedure是否存在
boolean exist = mapper.procedureOrFunctionIsExist("ifElseProcedure",database, procedureType);
// -> 不存在则创建存储过程
if (!exist) {
// 注:在java程序中创建MySQL存储过程时,直接写创建指令即可
String procedureSql = ("CREATE PROCEDURE ifElseProcedure ( IN paramA INT, IN paramB INT, "
+ "OUT paramC VARCHAR(40) ) ")
.concat("BEGIN ")
.concat(" IF paramA = 1 THEN set paramC = '参数paramA的值为1'; ")
.concat(" ELSEIF paramA = 2 THEN set paramC = '参数paramA的值为2' ; ")
.concat(" ELSEIF paramA = paramB THEN set paramC = '参数paramA的值 = 参数paramB的值' ; ")
.concat(" ELSE set paramC = '参数有误!' ; ")
.concat(" END IF; ")
.concat("END");
mapper.createProcedureOrFunction(procedureSql);
}
// -> 组装参数、(以map为参数容器)调用存储过程
Map<String, Object> map = new HashMap<>(4);
map.put("keyOne", 3);
map.put("keyTwo", 3);
// 执行调用
mapper.callProcedureByMap(map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
// -> 组装参数、(以model为参数容器)调用存储过程
ProcedureModel procedureModel = new ProcedureModel();
procedureModel.setParamA(4);
procedureModel.setParamB(5);
// 执行调用
mapper.callProcedureByModel(procedureModel);
System.out.println(procedureModel);
}
}
运行测试方法,控制台输出:
Java创建并使用函数示例:
涉及到的mapper中的接口有:
import com.aspire.model.ProcedureModel;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.StatementType;
import java.util.Map;
/**
* mapper注解
*
* 说明:灵活运用@Select等注解即可
*
*
* @author JustryDeng
* @date 2019/1/25 21:10
*/
@Mapper
public interface JustryDengMapper {
/**
* 查询 存储过程/函数 是否存在
*
* 注:mysql.proc记录的是所有数据库中的存储过程名,查询时最好指定数据库名;
* 当然如果能保证所有数据库中的存储过程的唯一性的话,也可以不指定数据库名
*
* @param name
* 存储过程/函数 的 名字
* @param database
* 数据库
* @param type
* 类型,其值只能为 PROCEDURE 或者 FUNCTION
*
* @return 是否存在
* @date 2019/1/27 10:16
*/
@Select("SELECT IF (COUNT(*)=0,FALSE,TRUE) FROM mysql.proc WHERE db=#{database} "
+ " AND `type`=#{type} AND `name`=#{name}")
boolean procedureOrFunctionIsExist (@Param("name") String name,
@Param("database") String database,
@Param("type") String type);
/**
* 创建 存储过程/函数 的SQL语句
*
* @param procedureOrFunction
* 存储过程/函数 的 创建语句
*
* @date 2019/1/25 21:17
*/
@Select("${procedureOrFunction}")
void createProcedureOrFunction (@Param("procedureOrFunction") String procedureOrFunction);
/**
* 调用函数
*
* 注:调用函数时的传参方式,与普通增删改查一样,返回值的接收也是一样的
*
* @param paramA
* 函数参数
* @param paramB
* 函数参数
*
* @return 函数结果
* @date 2019/1/27 10:35
*/
@Select("SELECT myFunction(#{a}, #{b})")
String invokeFunction (@Param("a") Integer paramA, @Param("b") Integer paramB);
}
测试方法为:
import com.aspire.mapper.JustryDengMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class MysqlFunctionProcessApplicationTests {
@Autowired
JustryDengMapper mapper;
/**
* java程序使用MySQL自定义函数示例
*
* @author JustryDeng
* @date 2019/1/27 10:24
*/
@Test
public void useFunctionDemo() {
final String database = "demo";
final String functionType = "FUNCTION";
// -> 判断自动以函数myFunction是否存在
boolean exist = mapper.procedureOrFunctionIsExist("myFunction", database, functionType);
// -> 不存在则创建函数
if (!exist) {
// 注:在java程序中创建函数时,直接写创建指令即可
String functionSql = "CREATE FUNCTION myFunction(paramA INT, paramB INT) "
.concat("RETURNS VARCHAR(50) ")
.concat("BEGIN")
.concat(" DECLARE diffValue INT;")
.concat(" DECLARE returnValue varchar(20) DEFAULT '';")
.concat(" set diffValue = paramA - paramB;")
.concat(" set returnValue = CONCAT(paramA,' - ', paramB, ' = ', diffValue);")
.concat(" RETURN returnValue;")
.concat("END");
mapper.createProcedureOrFunction(functionSql);
}
// -> 调用函数
System.out.println(mapper.invokeFunction(321, 123));
}
}
运行测试方法,控制台输出:
笔者寄语:
本文只是对MySQL存储过程与函数的简单使用示例,当存储过程、函数与实际业务联系起来后,可能会产生非常复杂的逻辑,就需要在实际开发中灵活处理了。