批量插入有兩個問題,第一是執行效率,第二數據沖突,第三數據重跑更新操作。
一般對於這樣的問題有以下操作方法。
第一是執行效率:mybatis支持兩種高效插入。
1.mybtis的foreach標簽,foreach元素的屬性主要有 item,index,collection,open,separator,close。
通過迭代把對應元素的屬性批量插入。
<insert id="batchInsert"> insert into day_time(daily_year,daily_month,daily_week,daily_date,use_num,types) values <foreach collection="list" item="item" separator=","> (#{item.dailyYear},#{item.dailyMonth},#{item.dailyWeek},#{item.dailyDate},#{item.useNum},#{item.types}) </foreach> </insert>
2.mybatis ExecutorType.BATCH
Mybatis內置的ExecutorType有3種 :SimpleExecutor、ReuseExecutor、BatchExecutor
默認的是SimpleExecutor查詢一次關閉一次每次查詢都會重新開啟statement,
ReuseExecutor用的不多,他不會關閉statement,以sql語句作為key相關的statement作為value,可以重復利用以前的創建好的statement,前提是sql必須一致,
也就是mybatis做boundsql解析之前的原生sql,不包括組裝的參數,sql相同組裝參數相同則代表解析boundsql一致一個session情況下是會直接走一級緩存直接調出結果。
然而ReuseExecutor使用的也就是很多數據連接池庫中常見的 PSCache 概念 。
BatchExecutor是正真的批量執行器,而batch模式重復使用已經預處理的語句,並且批量執行所有更新語句,顯然batch性能將更優;
但batch模式也有自己的問題,比如在Insert操作時,在事務沒有提交之前,是沒有辦法獲取到自增的id,這在某型情形下是不符合業務要求的。
但是批量模式下可以手動提交和回滾也是會有不同好處。
//獲取sqlsession //從spring注入原有的sqlSessionTemplate @Autowired private SqlSessionTemplate sqlSessionTemplate; // 新獲取一個模式為BATCH,自動提交為false的session // 如果自動提交設置為true,將無法控制提交的條數,改為最后統一提交,可能導致內存溢出 SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false); //通過新的session獲取mapper fooMapper=session.getMapper(FooMapper .class); int size = 10000; try { for (int i = 0; i < size; i++) { Foo foo = new Foo(); foo.setName(String.valueOf(System.currentTimeMillis())); fooMapper.insert(foo); if (i % 1000 == 0 || i == size - 1) { //手動每1000個一提交,提交后無法回滾 session.commit(); //清理緩存,防止溢出 session.clearCache(); } } }catch( Exception e) { //沒有提交的數據可以回滾 session.rollback(); }finally { session.close(); }
第二數據沖突,第三數據重跑更新操作
這兩個一起講,因為一般情況下,數據重跑也會存在數據沖突,一般就主鍵以及唯一索引沖突。
在批量更插入一般這樣的情況下,對於沖突的數據一般來說是要做更新操作的(前提是有做過自己的validation)。
但是又不可以方便對數據進行先查詢再決定進行插入操作還是更新操作,這樣批量操作時執行效率太低,。
mybatis批量中支持ON DUPLICATE KEY UPDATE用法。
也就是允許insert語句插入的行與表與現有記錄的惟一索引或主鍵中產生重復值,那么就會發生舊行的更新;
如果插入的行數據與現有表中記錄的唯一索引或者主鍵不重復,則執行新紀錄插入操作。
<insert id="batchInsert"> insert into day_time(daily_year,daily_month,daily_week,daily_date,use_num,types) values <foreach collection="list" item="item" separator=","> (#{item.dailyYear},#{item.dailyMonth},#{item.dailyWeek},#{item.dailyDate},#{item.useNum},#{item.types}) </foreach> <!-- 有則更新 --> ON DUPLICATE KEY UPDATE use_num = values(use_num) </insert>