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!
推薦閱讀:
- MyBatis批量插入的幾種方式效率比較
- MyBatis註解實現動態SQL問題
- mybatis 如何判斷list集合是否包含指定數據
- 基於mybatis 動態SQL查詢總結
- 一篇文章帶你瞭解mybatis的動態SQL