写在前面
最近在项目中需要一次性插入大批量数据到数据库,如果使用传统for循环方式一条记录一条记录地插入显然效率太低,而且大大增加了数据库的开销,因为每执行一次insert操作都要创建一次数据库连接。于是,想到了ibatis的批处理方法。
网上找了很多资料,大同小异,总结了一下,主要有一下两种方式:
- 在xml文件中使用iterator标签或foreach标签拼接sql
- 使用sqlMapClient提供的一组批处理方法
使用xml拼接sql
xml代码如下:
<insert id="insertData" parameterClass="">
insert into batchinsert_test(id,
studentid,
schoolid,
schoolname,
supplierid,
countryid,
major,
payerid,
schinesename,
senglishsurname
)
select hibernate_sequence.nextval, t.* from ( // 使用序列生成主键
<iterate conjunction=" 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
</iterate>
) t
</insert>
注意:此语法只支持Oracle,不支持mysql,mysql的自行百度,原理是一样的,只是语法不一样
调用代码如下:
@Override
public void insertData(List<batchInsertEntity> list) {
try {
this.("", list);
} catch (SQLException e) {
// TODO Auto-generated catch block
();
}
}
测试发现,使用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<batchInsertEntity> list) {
// list入参必须声明为final,不能被修改
// 执行回调
getSqlMapClientTemplate().execute(new SqlMapClientCallback<Object>() {
//实现回调接口
@Override
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
int batch = 0;
();// 开始批处理
for (batchInsertEntity entity : list) {
batch++;
("OrderformImport.insertData2", entity);
//每100条提交一次
if (batch%100 == 0) {//Oracle 11g推荐的batch大小介于50-100之间
();
();
}
}
();// 执行批处理
return null;
}
});
}
}
最终可能抛出的异常:DataAccessException
<insert id="insertData2" parameterClass="">
<selectKey resultClass="Long" type="pre" keyProperty="id">
select hibernate_sequence.nextval from dual
</selectKey>
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#
)
</insert>
测试了10万条数据,导入正常,耗时大概4分钟多点,在实际项目中,如果数据量大,建议每1000条(这个根据自己的实际情况自己定)就做一次批量导入,这样效率会比一次性批量导入高很多。