MyBatis批量插入大量數據(1w以上)

問題背景:隻用MyBatis中foreach進行批量插入數據,一次性插入超過一千條的時候MyBatis開始報錯。項目使用技術:SpringBoot、MyBatis

批量插入碰到的問題:

java.lang.StackOverflowError: null

該問題是由於一次性插入數據1w條引起的,具體插入代碼如下:

userDao.batchInsert(list);

<insert id="batchInsert" parameterType="java.util.List">
    INSERT INTO USER
    <trim prefix="(" suffix=")" suffixOverrides=",">
        ID, AGE, NAME, EMAIL
    </trim>
    SELECT A.*
    FROM
    (<foreach collection="list" index="index" item="item" separator="UNION ALL">
    SELECT
    sys_guid(), #{user.age}, #{user.name}, #{user.email}
    FROM dual
</foreach>) A
</insert>

以上的插入代碼其實也是一種批量插入的方式,但是他的靈界點並不高,插入數據過多的時候,可能需要我們使用代碼在一次分批。當然如果插入數據不超過5000的時候可以直接這麼使用

插入1w條數據,發現出現錯誤,原因是數據量過大,棧內存溢出瞭。mybatis中直接使用foreach插入數據,就相當於將所有的sql預先拼接到一起,然後一起提交。這本身就是一種批量插入的處理方案,但是達不到我們要求。主要是插入有上限。如果需要更多的數據導入,我們需要更換一種方式來解決這個問題,mybatis中ExecutorType的使用。

mybatis中ExecutorType的使用

Mybatis內置的ExecutorType有3種,SIMPLE、REUSE、BATCH; 默認的是simple,該模式下它為每個語句的執行創建一個新的預處理語句,單條提交sql;而batch模式重復使用已經預處理的語句,並且批量執行所有更新語句,顯然batch性能將更優;但batch模式也有自己的問題,比如在Insert操作時,在事務沒有提交之前,是沒有辦法獲取到自增的id,這在某型情形下是不符合業務要求的;

插入大量數據的解決方案,使用ExecutorType

為瞭能夠高效,並且解決上述問題,我們使用ExecutorType,並分批插入。代碼如下:

//我們使用的是springboot,sqlSessionTemplate是可以自己註入的
@Autowired
private SqlSessionTemplate sqlSessionTemplate;

public void insertExcelData(List<User> list) {
    //如果自動提交設置為true,將無法控制提交的條數,改為最後統一提交,可能導致內存溢出
    SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
    //不自動提交
    try {
        UserDao userDao = session.getMapper(UserDao.class);
        for (int i = 0; i < list.size(); i++) {
            userDao.insert(list.get(i));
            if (i % 400 == 0 || i == list.size() - 1) {
                //手動每400條提交一次,提交後無法回滾
                session.commit();
                //清理緩存,防止溢出
                session.clearCache();
            }
        }
    } catch (Exception e) {
        //沒有提交的數據可以回滾
        session.rollback();
    } finally {
        session.close();
    }
}

userDao.insert(User user);

<insert id="insert" parameterType="com.echo.UserPo">
    insert into USER
    (id
    <if test="age != null">
        ,age
    </if>
    <if test="name != null">
        ,name
    </if>
    <if test="email != null">
        ,email
    </if>
    )
    values (
    sys_guid()
    <if test="age != null">
        ,#{age}
    </if>
    <if test="name != null">
        ,#{name}
    </if>
    <if test="email != null">
        ,#{email}
    </if>)
</insert>

這裡采用的是單條插入,直接使用for循環,但是使用ExecutorType.BACTH就相當於手動提交。這也是我們需要的效果,所以我們在循環裡面判斷瞭,是否到瞭第400筆,如果到瞭第400筆就直接提交,然後清空緩存,防止溢出。這樣就有效的實現瞭批量插入,同時保證溢出問題的不出現

到此這篇關於MyBatis批量插入大量數據(1w以上)的文章就介紹到這瞭,更多相關MyBatis批量插入內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: