1. 程式人生 > 實用技巧 >iBatis批量插入1萬多條資料,出錯後繼續執行剩下的程式碼

iBatis批量插入1萬多條資料,出錯後繼續執行剩下的程式碼

翻了很多大佬的博文,得到覺得最佳的方式:基於回撥的方式

回撥介面SqlMapClientCallback,這個是沒有事務的。

這裡有一位大佬對iBatis原始碼的解析:

https://www.cnblogs.com/jdluojing/p/4201832.html#4686042

由於這個是直接操作資料庫的,所以我寫在DAO層。

我想要實現的需求:

  資料來源是從一個介面一次性獲得到的,這個不改;

  然後批量入庫,出錯後繼續執行剩下的程式碼!

 sqlMapClientTemplate.execute(new SqlMapClientCallback(){
            @Override
            
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { executor.startBatch(); int count = 1; //記錄操作的數量 Account account= new account(); for(CustResponse custRes : custResponseList) { account.setAcctId(custRes.getAcctId()); account.setCustId(custRes.getCustId()); account.setCustName(custRes.getCustName()); executor.insert(sqlName, account);//sqlName="com........Account.mergeIntoAccount"
try { if (count % AccountTblImpl.BATCH_NUM == 0) { //AccountTblImpl.BATCH_NUM = 50 executor.executeBatch(); executor.startBatch(); } } catch (Exception e) { LOG.error(
"定時同步數倉資料: 第" + (count-AccountTblImpl.BATCH_NUM+1) + "-" + count + "資料入庫發生異常!", e); printErrorCustList(custResponseList, count-AccountTblImpl.BATCH_NUM, count); } count++; } try { executor.executeBatch(); } catch (Exception e) { int fromIndex = count/AccountTblImpl.BATCH_NUM*accountTblImpl.BATCH_NUM; LOG.error("定時同步數倉資料: 第" + (fromIndex+1) + "-" + (count-1) + "資料入庫發生異常!", e); printErrorCustList(custResponseList, fromIndex, count-1); } //Oracle沒有辦法知道batch中某語句確切影響的記錄數,而JDBC 2.0規範規定,操作成功但影響行數不確定的 //所以executor.executeBatch() 返回0 return 0; } });
//這個是後面加的,因為實在沒有找到辦法可以定位到發生異常的那條資料,所以把發生異常的這個批次全部記錄打印出來
public void printErrorCustList(List<CustResponse> custResponseList, int fromIndex, int toIndex) {
   List<CustResponse> errorCustList = custResponseList.subList(fromIndex, toIndex);
   LOG.error("發生異常的這批資料:" + errorCustList);
}

1w多條資料,正常執行的時間是在34秒-37秒,在伺服器上跑也是正常情況。

Oracle資料操作程式碼

<statement id="mergeIntoAccount" parameterClass="AccountClass">
    MERGE INTO ACCOUNT
    USING (
        select count(1) co from ACCOUNT where ACCT_ID = #acctId:VARCHAR#
    ) S ON ( S.co = 1 )
    WHEN MATCHED THEN
    update set
        CUST_ID = #custId:VARCHAR#,
        CUST_NAME = #custName:VARCHAR#,
        UPDATE_TIME = to_timestamp(to_char(sysdate,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')
        WHERE ACCT_ID = #acctId:VARCHAR#
    WHEN NOT MATCHED THEN
    INSERT (
        ACCT_ID, CUST_ID, CUST_NAME, UPDATE_TIME
    ) VALUES (
        #acctId:VARCHAR#, #custId:VARCHAR#, #custName:VARCHAR#,
        to_timestamp(to_char(sysdate,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS')
    )
</statement>

--------------------------------- 以下是自己作的流程,僅供自己回憶-----------------------------------------

那麼問題來了,我查不到這個批處理在併發的情況下會是什麼情況?

然後我手動設定了3個錯誤的資料:

List<CustResponse> List = custListRes.getData();
CustResponse c = List.get(9);
LOG.error("即將出錯的資料:" + c);
c.setAcctId(null);
CustResponse c2
= List.get(1000); LOG.error("即將出錯的資料:" + c2); c2.setAcctId(null);
CustResponse c3
= List.get(13101); LOG.error("即將出錯的資料:" + c3); c3.setAcctId(null);

單單測試這段程式碼是按照預期想法來的,以下是log日誌:

//dao層日誌
定時同步數倉資料: 第1-50資料入庫發生異常! 定時同步數倉資料: 第1001-1050資料入庫發生異常! 定時同步數倉資料: 第13101-13102資料入庫發生異常!

//service層日誌
定時同步數倉資料[通過iBatis批處理]—執行完畢!耗時:36秒

然後這段業務是個定時任務,因為專案中還有其他6,7個定時任務在跑,這些任務都有對這個資料表進行操作,

然後這個批處理就扛不住了,輸出了第二段錯誤日誌後【定時同步數倉資料: 第1001-1050資料入庫發生異常!】

就沒有反應了,隔了很久才打出最後結束的日誌(當場狗帶)

不知道為什麼,批處理出現異常後,效能天差地別。

下班時間到了,又搞了2小時,才發現當時卡主是因為本地在跑,伺服器也在跑,都是在做插入操作!

本地停了之後,伺服器的日誌列印的時間就恢復正常了!唉......