为了不重复写 mapper 文件里的foreach
标签,我们通常会将mybatis
升级为mybatis-plus
,从而使用Iservice
里面的saveBatch
方法;
一、foreach标签用法
<select id="getUserInfo" resultType="com.test.UserList">
SELECT
*
FROM user_info
where
<if test="userName!= null and userName.size() >0">
USERNAME IN
<foreach collection="userName" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</if>
</select>
二、saveBatch用法
集成serviceImpl:
public class ReportServiceImpl extends ServiceImpl<ReportDao,ReportPo> implements IReportService
调用this.saveBatch()方法:
this.saveBatch(list);
三、出现的问题
在生产使用中经过实测发现这个savebatch
方法比单个的insert
效率还慢
源码:
try {
clearWarnings();
if (!this.batchHasPlainStatements
&& this.connection.getRewriteBatchedStatements()) {
if (canRewriteAsMultiValueInsertAtSqlLevel()) {
return executeBatchedInserts(batchTimeout);
}
if (this.connection.versionMeetsMinimum(4, 1, 0)
&& !this.batchHasPlainStatements
&& this.batchedArgs != null
&& this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
return executePreparedBatchAsMultiStatement(batchTimeout);
}
}
return executeBatchSerially(batchTimeout);
} finally {
clearBatch();
}
可以看出有一个参数能帮助我们更快的确定mysql的batch工作机制,那就是mysql jdbc driver 的connection url, 其中有一个参数是: rewriteBatchedStatements
rewriteBatchedStatements 参数默认为false, 需要手工设置为true,设置方式:
url:jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true
默认状态时rewriteBatchedStatements=false,执行路径会跳到 executeBatchSerially,此方法内部将语句一条条发送,与非batch处理简直一样,所以效率低;
当设为 true时,会执行executeBatchedInserts方法,事实上mysql支持这样的插入语句:insert into t_user(id,uname) values(1, '1'), (2,'2'), (3, '3') ...
四、总结
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
这个选项对INSERT/UPDATE/DELETE都有效