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小时,才发现当时卡主是因为本地在跑,服务器也在跑,都是在做插入操作!

本地停了之后,服务器的日志打印的时间就恢复正常了!唉......

原文地址:https://www.cnblogs.com/hxliang/p/13687196.html