1.需求描述

公司需要在定时任务中处理大量数据,最后更新的数据量可能有几万条。所以需要进行批量更新。

2.mybatis批量更新

先贴出批量更新sql

<update id="batchUpdate" parameterType="java.util.List">
		update fm_news_newsdynamic
		<trim prefix="set" suffixOverrides=",">
			<trim prefix="review_count =case" suffix="end,">
				<foreach collection="list" item="item" index="index">
					<if test="item.reviewCount !=null">
						when news_id=#{item.newsId} then review_count+#{item.reviewCount}
					</if>
				</foreach>
			</trim>
			<trim prefix="collect_count =case" suffix="end,">
				<foreach collection="list" item="item" index="index">
					<if test="item.collectCount !=null">
						when news_id=#{item.newsId} then collect_count+#{item.collectCount}
					</if>
				</foreach>
			</trim>
		</trim>
		where news_id in
		<foreach collection="list" index="index" item="item" separator="," open="(" close=")">
			#{item.newsId}
		</foreach>
	</update>

上面这个sql只是拿两个字段进行示范,实际更新的字段更多。

然后,我们来解析一下这些sql标签是什么意思

<trim prefix="set" suffixOverrides=",">

使用<trim>标签动态构建SET部分,prefix="set"表示前缀为setsuffixOverrides=","表示删除最后一个逗号。

更新review_count字段的case语句:


  1. <trim prefix="review_count = case" suffix="end,">:

动态构建review_countcase语句,前缀为review_count = case,后缀为end,


  1. <foreach collection="list" item="item" index="index">:

遍历参数列表listitem表示每个元素,index表示索引。


  1. <if test="item.reviewCount != null">:

仅当reviewCount不为null时生成相应的SQL。


  1. when news_id = #{item.newsId} then review_count + #{item.reviewCount}:

对于每个item,根据news_id匹配,更新review_count

collect_count字段和review_count字段更新逻辑相同

解析后的sql如下:

update fm_news_newsdynamic
set
    review_count = case
        when news_id = 1 then review_count + 10
        when news_id = 2 then review_count + 20
    end,
    collect_count = case
        when news_id = 1 then collect_count + 5
        when news_id = 3 then collect_count + 15
    end
where news_id in (1, 2, 3)

解析后的sql,虽然不是单纯的拼接update语句,但也需要对更新字段进行case when判断。如果news_id很多的话,解析后的sql也是很庞大的。

所以在service层我们还可以对处理的list集合进行分割。用分割后小的list进行批量更新。

2.Lists.partition集合分割

我们处理的list如果数据量比较大,可以对它进行分割。可以进行手动分割,先设定每个小的list大小,然后根据总的数量计算有多少个小的list,不过这样比较麻烦。

Google Guava 库中有一个静态方法partition,用于将一个大的列表分割成多个大小相等的子列表。如果原始列表的大小不能被分割大小整除,则最后一个子列表的大小可能会小于指定的大小。这在处理大数据集或需要对列表进行批量操作时非常有用。

假设我们有一个列表,需要将其分割成大小为 3 的子列表:

import com.google.common.collect.Lists;

import java.util.List;

public class Example {
    public static void main(String[] args) {
        List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        List<List<Integer>> partitioned = Lists.partition(numbers, 3);
        for (List<Integer> part : partitioned) {
            System.out.println(part);
        }
    }
}
//输出结果如下:
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]