背景:项目中需要使用存储过程做积分与余额的转化,需要传入类型来决定处理哪种类型的账户进行转化,也需要接收转化数目。存储过程已经写好,但是一直接收不到存储过程的返回值,纠结了很久终于解决,在网上搜索的处理方式也都是看的一知半解,所以做下记录。
DROP PROCEDURE pro_member_credits_transfer;
CREATE PROCEDURE pro_member_credits_transfer(IN accountType int,OUT transferAmount int)
BEGIN
DECLARE vcreditsId int(20);
DECLARE vmemberId int(20) ;
DECLARE vnowCredits decimal(20,4);
DECLARE vlevel int(10);
DECLARE vrate decimal(10,4);
DECLARE accountCount int DEFAULT(1); #账户数量 需要转化的积分账户数量
DECLARE startNum INT DEFAULT 0; #开始位置
DECLARE done INT DEFAULT FALSE; #结束标志变量
#DECLARE total int default 0; #总条数变量
DECLARE coin decimal(10,4); #积分转微币数目
DECLARE nowCoin decimal(20,4); #当前微币数目
DECLARE coinId int(20); #微币账户ID
DECLARE xact_abort int;
#声明游标
DECLARE credits_cur CURSOR FOR SELECT
id,member_id,now_credits,level,IFNULL((select transfer_rate from common_coin_transfer_config where member_level = level),0.03) as rate
FROM
common_coin_credits
WHERE status = 0 and account_type = accountType and now_credits > 0.3 and transfer_date != (select DATE_FORMAT(SYSDATE(),'%Y-%m-%d') from dual) for update;
#将结束标志绑定到游标,若没有数据返回,程序继续,并将变量done设为TRUE
DECLARE CONTINUE HANDLER FOR NOT FOUND
SET done = TRUE;
#捕获执行过程中异常
DECLARE continue HANDLER FOR SQLEXCEPTION
GET DIAGNOSTICS CONDITION 1
@errno = MYSQL_ERRNO, @errmsg = MESSAGE_TEXT;
SELECT count(1) into accountCount from common_coin_credits where status = 0 and account_type = accountType and now_credits > 0.3;
SET transferAmount = 0;
START TRANSACTION;
OPEN credits_cur;
read_loop:loop
fetch credits_cur into vcreditsId,vmemberId,vnowCredits,vlevel,vrate;
#判断游标的循环是否结束
if done then
leave read_loop; #跳出游标循环
end if;
#获取一条数据时,将count值进行累加操作,这里可以做任意你想做的操作,
set transferAmount = transferAmount + 1;
#计算积分转微币数目
set coin = vnowCredits*vrate/100;
if coin >= 0.0001 THEN
#查找该会员对应的微币账户 并获取微币账户ID
select id,now_coin into coinId,nowCoin from common_coin_coin where member_id = vmemberId and account_type = accountType for update;
#更新微币账户表
update common_coin_coin set now_coin = nowCoin+coin, today_increase = coin,today_decrease = 0, update_time = SYSDATE() where id = coinId ;
#插入微币变更日志
insert into common_coin_coin_log(coin_id, now_coin, coin, child_type, create_time, remark) VALUES (coinId,nowCoin+coin,coin,'CA',SYSDATE(),CONCAT('用户积分转微币,微币收益:',coin));
#修改积分账户
update common_coin_credits set now_credits = now_credits-coin, today_decrease = coin ,today_increase = 0 ,update_time = SYSDATE(),transfer_date = SYSDATE() where id = vcreditsId;
#记录积分变更日志
insert into common_coin_credits_log(credits_id, now_credits, credits, child_type, create_time, remark) VALUES (vcreditsId,vnowCredits-coin,-coin,'BRS',SYSDATE(),CONCAT( '用户积分转微币,积分扣减:',coin));
#结束游标循环
else
ITERATE read_loop;
end if;
end loop;
#关闭游标
close credits_cur;
IF @errno>0 THEN
SELECT @errno,@errmsg;
set transferAmount = -1;
ROLLBACK;-- 事务回滚
ELSE
SELECT 'success' as result;
COMMIT;-- 事务提交
END IF;
end;
存储过程很简单,可以做下参考
这里定义执行方法,其中有几个重要的点:
1、parameterType 参数类型一定要为java.util.Map类型
2、要增加属性 statementType="CALLABLE" 来标识这是调用存储过程
3、返回值类型需要定义
其他的遵从Mybatis规范即可 参数顺序需要和存储过程中的保持一直,入参用:mode=IN 来标识 出参用 mode=OUT 标识
@Scheduled(cron = "1 0 2 * * ?")
public void transferCredits(){
Map map = new HashMap<>();
map.put("accountType", Constants.ACCOUNT_TYPE_MEMBER);
int retryCount = 0;
try {
transferCreditsDao.transferCredits(map);
System.out.println("转化总数:"+map.get("transferAmount"));
while (Integer.valueOf(map.get("transferAmount").toString()) == -1) {
transferCreditsDao.transferCredits(map);
retryCount++;
if (retryCount >=3)
break;
}
} catch (Exception e) {
if (retryCount <3)
transferCreditsDao.transferCredits(map);
}
}
这里在执行存储过程后 通过我们定义的入参的Map的get(key)方法来获取对应的返回值 ,其中的key值为我们定义的出参名。
下面是测试结果
{conn-10001, cstmt-20001} enter cache
[DEBUG] 2019-11-14 21:29:21,656 method:com.alibaba.druid.filter.logging.Log4jFilter.connectionLog(Log4jFilter.java:132)
{conn-10001} commited
[DEBUG] 2019-11-14 21:29:21,658 method:com.alibaba.druid.filter.logging.Log4jFilter.connectionLog(Log4jFilter.java:132)
{conn-10001} pool-recycle
转化总数:109
到此使用使用Mybatis获取存储过程的出参 的流程已经走完了,有不清楚的地方欢迎留言一起讨论学习。