前言:项目中遇到批量新增和更新的业务,大概更新2万条,新增3万条。之前为了进度,直接使用mybatis-plus自带的批量新增(saveBatch)和更新(saveOrUpdateBatch)执行时间60秒,直接奔溃了。摒弃mybatis自带的批量处理方式,自己写xml语句,执行时间缩短为5秒左右,优化方案如下:
sql连接配置添加&allowMultiQueries=true,例如
url: jdbc:mysql://127.0.0.1/med?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&allowMultiQueries=true
批量更新xml语句:
<update id="updateBatch">
<foreach collection="list" item="item" separator=";">
update kl_diagnose_detail
<set>
<if test="item.verifyStandard != null">
verify_standard = #{item.verifyStandard},
</if>
<if test="item.verifyRelation != null">
verify_relation = #{item.verifyRelation},
</if>
</set>
where id = #{item.id}
</foreach>
</update>
注意:另外还有一种写法是使用case…when,代码如下【效率低下】:
<update id="updateBatch">
update kl_diagnose_detail
<trim prefix="set" suffixOverrides=",">
<trim prefix="verify_standard=case" suffix="end,">
<foreach collection="list" item="item" index="index">
<if test="item.verifyStandard!=null">
when id=#{item.id} then #{item.verifyStandard}
</if>
</foreach>
</trim>
<trim prefix="verify_relation=case" suffix="end,">
<foreach collection="list" item="item" index="index">
<if test="item.verifyRelation!=null">
when id=#{item.id} then #{item.verifyRelation}
</if>
</foreach>
</trim>
</trim>
where
<foreach collection="list" separator="or" item="item" index="index">
id=#{item.id}
</foreach>
</update>
经本人亲自测试之后,效率远不如第一种写法,而且数据量越大,大量的case…when判断使得执行效率越低,不可取。
批量新增xml语句:
<insert id="insertBatch">
INSERT INTO kl_diagnose_question(gmt_create, gmt_modified, diagnose_id) VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.gmtCreate},#{item.gmtModified},#{item.diagnoseId}
)
</foreach>
</insert>
另外还有一种写法如下【效率低下】,不可取
<insert id="insertBatch">
<foreach collection="list" item="item" separator=";">
INSERT INTO kl_diagnose_question(gmt_create, gmt_modified, diagnose_id, dis_name, `type`, question_name, question_type, order_no) VALUES
(
#{item.gmtCreate},#{item.gmtModified},#{item.diagnoseId},#{item.disName},#{item.type},#{item.questionName},#{item.questionType},#{item.orderNo}
)
</foreach>
</insert>
特别注意
如果批量操作的数据特别大,在拼接sql时可能会报堆栈溢出,处理如下:
将原有的List<T> 类型,拆分成List<List<T>>,方法如下,通过循环执行就不会报错。
// 总体代码结构如下
for (int i = 0; i < size; i++) {
// 批量执行代码;
}
/**
* sql批量执行时,参数分组后按组提交,避免游标、数据量超过限制
*
* @param originList 原始参数列表
* @param capacity 分组后每组的大小
* @return List<List < T>> 分组后的参数列表
*/
public static <T> List<List<T>> divideList(List<T> originList, int capacity) {
List<List<T>> list = new LinkedList<>();
int originListSize = originList.size();
int length = originListSize / capacity;
if (length == 0) {
list.add(originList);
return list;
}
if (originListSize % capacity > 0) {
length = length + 1;
}
for (int i = 0; i < length; i++) {
int fromIndex = i * capacity;
int toIndex = (i + 1) * capacity > originListSize ? originListSize : (i + 1) * capacity;
list.add(new ArrayList<T>(originList.subList(fromIndex, toIndex)));
}
return list;
}