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>
对比下一条一条插入: 1w多条数据插入时间为65秒左右;
不知道为什么放到服务器上,耗时变为7-8min,所以就放弃了这种方法。
iBatis批处理的话,BATCH_NUM 为500时,也要60秒每次,越大时间越短。
我自己测试是这样的。10000条/批的时候是38秒左右。
--------------------------------- 以下是自己作的流程,仅供自己回忆-----------------------------------------
那么问题来了,我查不到这个批处理在并发的情况下会是什么情况?
然后我手动设置了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小时,才发现当时卡主是因为本地在跑,服务器也在跑,都是在做插入操作!
本地停了之后,服务器的日志打印的时间就恢复正常了!唉......