ibatis批量插入数据

写在前面

最近在项目中需要一次性插入大批量数据到数据库,如果使用传统for循环方式一条记录一条记录地插入显然效率太低,而且大大增加了数据库的开销,因为每执行一次insert操作都要创建一次数据库连接。于是,想到了ibatis的批处理方法。

网上找了很多资料,大同小异,总结了一下,主要有一下两种方式:

  • 在xml文件中使用iterator标签或foreach标签拼接sql
  • 使用sqlMapClient提供的一组批处理方法

使用xml拼接sql

xml代码如下:

"insertData" parameterClass="java.util.List">
        insert into batchinsert_test(id,
              studentid,
              schoolid,
              schoolname,
              supplierid,
              countryid,
              major,
              payerid,
              schinesename,
              senglishsurname
        ) 
        select hibernate_sequence.nextval, t.* from (  // 使用序列生成主键
            " union all " > // conjunction属性表示每次循环结束添加的字符
                select 
                       #list[].studentid#,  // list为传入的参数名称
                       #list[].schoolid#,
                       #list[].schoolname#,
                       #list[].supplierid#,
                       #list[].countryid#,
                       #list[].major#,
                       #list[].payerid#,
                       #list[].schinesename#,
                       #list[].senglishsurname#
                from dual
            
        ) t

注意:此语法只支持Oracle,不支持mysql,mysql的自行百度,原理是一样的,只是语法不一样

调用代码如下:

@Override
    public void insertData(List list) {
        try {
            this.sqlMapClient.insert("OrderformImport.insertData", list);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

测试发现,使用xml拼接sql的方式实现批量插入数据有一个问题,当批量插入的数据量太大(我的是超过5000条数据),会报“无效的主机/绑定变量名”异常,原因未明,还望知道的大神不吝赐教,楼主不胜感激。

使用sqlMapClient提供的批处理方法

ibatis通过sqlMapClient提供了一组方法用于实现批处理:

  • startBatch():开始批处理
  • executeBatch():执行批处理

为了避免事务代码的入侵,把事务完全交给Spring统一处理,我们使用Spring的回调模式去实现:

首先你的dao需要继承SqlMapClientDaoSupport类,或者在你的dao中将sqlMapClient注入到sqlMapClientTemplate中,然后调用sqlMapClientTemplate的execute方法,覆写SqlMapClientCallback类中的doInSqlMapClient方法

@Repository("testDao")
public class TestDaoImpl extends SqlMapClientDaoSupport implements TestDao {

    @Override
    public void insertBatchData(final List list) {
        // list入参必须声明为final,不能被修改
        // 执行回调
        getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
            //实现回调接口
            @Override
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                int batch = 0;
                executor.startBatch();// 开始批处理
                for (batchInsertEntity entity : list) {
                    batch++;
                    executor.insert("OrderformImport.insertData2", entity);
                    //每100条提交一次
                    if (batch%100 == 0) {//Oracle 11g推荐的batch大小介于50-100之间
                        executor.executeBatch();
                        executor.startBatch();
                    }
                }
                executor.executeBatch();// 执行批处理

                return null;
            }
        });
    }
} 
  

最终可能抛出的异常:DataAccessException

id="insertData2" parameterClass="com.iss.itreasury.cbeservice.transaction.orderformimport.web.batchInsertEntity">
        "Long" type="pre" keyProperty="id">
            select hibernate_sequence.nextval from dual
        

        insert into batchinsert_test(id,
            studentid,
              schoolid,
              schoolname,
              supplierid,
              countryid,
              major,
              payerid,
              schinesename,
              senglishsurname
        )
        values(#id#,
            #studentid#,
              #schoolid#,
              #schoolname#,
              #supplierid#,
              #countryid#,
              #major#,
              #payerid#,
              #schinesename#,
              #senglishsurname#
        )
    

测试了10万条数据,导入正常,耗时大概4分钟多点,在实际项目中,如果数据量大,建议每1000条(这个根据自己的实际情况自己定)就做一次批量导入,这样效率会比一次性批量导入高很多。

你可能感兴趣的:(java)