背景:項目中需要使用存儲過程做積分與餘額的轉化,需要傳入類型來決定處理哪種類型的賬戶進行轉化,也需要接收轉化數目。存儲過程已經寫好,但是一直接收不到存儲過程的返回值,糾結了很久終於解決,在網上搜索的處理方式也都是看的一知半解,所以做下記錄。
一、存儲過程:
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;
存儲過程很簡單,可以做下參考
二、Mybatis的Mapper文件代碼:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.common.task.dao.TransferCreditsDao">
<select id="transferCredits" parameterType="java.util.Map" statementType="CALLABLE" resultType="java.util.Map">
{call pro_member_credits_transfer(
#{accountType,mode=IN,jdbcType=INTEGER},#{transferAmount,mode=OUT,jdbcType=INTEGER})}
</select>
</mapper>
這裏定義執行方法,其中有幾個重要的點:
1、parameterType 參數類型一定要爲java.util.Map類型
2、要增加屬性 statementType="CALLABLE" 來標識這是調用存儲過程
3、返回值類型需要定義
其他的遵從Mybatis規範即可 參數順序需要和存儲過程中的保持一直,入參用:mode=IN 來標識 出參用 mode=OUT 標識
三、在Service層接收返回值:
@Scheduled(cron = "1 0 2 * * ?")
public void transferCredits(){
Map<String, Object> 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獲取存儲過程的出參 的流程已經走完了,有不清楚的地方歡迎留言一起討論學習。