Java8实现批量新增

433 阅读2分钟

本文记录一个利用Java8批量新增数据的案例,当数据大于10000条的时候,此方法的耗时明显小于常规线性插入的方式。当导入的数据量比较大时,很容易发生java.lang.OutOfMemorycom.mysql.cj.jdbc.exceptions.PacketTooBigException异常,我们可以调整JVM以及MySQL配置文件的方式,同时通过使用Java8的特性,可以提高一次性倒入数据的上限。 我们建好对应的实体类ItemInfo,该实体类有主键id,以及96个item项。数据库中有对应的表。

@Data 
public class ItemInfo {
    private String item1; 
    private String item2; 
    private String item3; 
    private String item4; 
    private String item5; 
    private String item6; 
    private String item7; 
    private String item8; 
    private String item9; 
    private String item10; 
    private String item11; 
//...... 
//共有96项

mapper文件中,使用常规的循环插入

<insert id="insertItem" parameterType="list">
    INSERT INTO item_info
    (id, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10,
        item11, item12, item13, item14, item15, item16, item17, item18, item19, item20,
        item21, item22, item23, item24, item25, item26, item27, item28, item29, item30,
        item31, item32, item33, item34, item35, item36, item37, item38, item39, item40,
        item41, item42, item43, item44, item45, item46, item47, item48, item49, item50,
        item51, item52, item53, item54, item55, item56, item57, item58, item59, item60,
        item61, item62, item63, item64, item65, item66, item67, item68, item69, item70,
        item71, item72, item73, item74, item75, item76, item77, item78, item79, item80,
        item81, item82, item83, item84, item85, item86, item87, item88, item89, item90,
        item91, item92, item93, item94, item95, item96
    )
    values
    <foreach collection="list" item="item" index="index" separator=",">
        (
        null,
        #{item.item1}, #{item.item2}, #{item.item3}, #{item.item4}, #{item.item5}, #{item.item6}, #{item.item7}, #{item.item8}, #{item.item9}, #{item.item10},
        #{item.item11}, #{item.item12}, #{item.item13}, #{item.item14}, #{item.item15}, #{item.item16}, #{item.item17}, #{item.item18}, #{item.item19}, #{item.item20},
        #{item.item21}, #{item.item22}, #{item.item23}, #{item.item24}, #{item.item25}, #{item.item26}, #{item.item27}, #{item.item28}, #{item.item29}, #{item.item30},
        #{item.item31}, #{item.item32}, #{item.item33}, #{item.item34}, #{item.item35}, #{item.item36}, #{item.item37}, #{item.item38}, #{item.item39}, #{item.item40},
        #{item.item41}, #{item.item42}, #{item.item43}, #{item.item44}, #{item.item45}, #{item.item46}, #{item.item47}, #{item.item48}, #{item.item49}, #{item.item50},
        #{item.item51}, #{item.item52}, #{item.item53}, #{item.item54}, #{item.item55}, #{item.item56}, #{item.item57}, #{item.item58}, #{item.item59}, #{item.item60},
        #{item.item61}, #{item.item62}, #{item.item63}, #{item.item64}, #{item.item65}, #{item.item66}, #{item.item67}, #{item.item68}, #{item.item69}, #{item.item70},
        #{item.item71}, #{item.item72}, #{item.item73}, #{item.item74}, #{item.item75}, #{item.item76}, #{item.item77}, #{item.item78}, #{item.item79}, #{item.item80},
        #{item.item81}, #{item.item82}, #{item.item83}, #{item.item84}, #{item.item85}, #{item.item86}, #{item.item87}, #{item.item88}, #{item.item89}, #{item.item90},
        #{item.item91}, #{item.item92}, #{item.item93}, #{item.item94}, #{item.item95}, #{item.item96}
        )
    </foreach>
</insert>

DAO层:

int insertItem(List<ItemInfo> list);

核心代码是一个方法类,将DAO层的方法作为一个参数传递到Consumer中,其中size可以根据实际情况调整:

public class InsertConsumer {
    private final static int SIZE = 1000;

    static {
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "5");
    }

    public static <T> void insertData(List<T> list, Consumer<List<T>> consumer) {
        if (list == null || list.size() < 1) {
            return;
        }

        List<List<T>> streamList = new ArrayList<>();

        for (int i = 0; i < list.size(); i += SIZE) {
            int j = Math.min((i + SIZE), list.size());
            List<T> subList = list.subList(i, j);
            streamList.add(subList);
        }
        streamList.parallelStream().forEach(consumer);
    }
}

在service层调用这个方法:

@Override
public int insertItemStream(List<ItemInfo> list) {
    try {
        InsertConsumer.insertData(list, infoDao::insertItem);
        return 1;
    } catch(Exception e) {
        e.printStackTrace();
    }
    return 0;
}