iBatis批量插入1萬多條資料,出錯後繼續執行剩下的程式碼
阿新 • • 發佈:2020-09-17
翻了很多大佬的博文,得到覺得最佳的方式:基於回撥的方式
回撥介面SqlMapClientCallback,這個是沒有事務的。
這裡有一位大佬對iBatis原始碼的解析:
https://www.cnblogs.com/jdluojing/p/4201832.html#4686042
由於這個是直接操作資料庫的,所以我寫在DAO層。
我想要實現的需求:
資料來源是從一個介面一次性獲得到的,這個不改;
然後批量入庫,出錯後繼續執行剩下的程式碼!
sqlMapClientTemplate.execute(new SqlMapClientCallback(){ @Overridepublic 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小時,才發現當時卡主是因為本地在跑,伺服器也在跑,都是在做插入操作!
本地停了之後,伺服器的日誌列印的時間就恢復正常了!唉......