Mybatis 批量操作8种实现总结
批量新增
1.方式一(常用)
<!-- 批量新增--> <insert id="batchSave" parameterType="java.util.List">INSERT INTO lp_user_test_batch(id,user_id,user_name,user_age,type,create_time,update_time)VALUES<foreach collection="list" item="item" index="index" separator=",">(#{item.id,jdbcType=BIGINT},#{item.userId,jdbcType=VARCHAR},#{item.userName,jdbcType=VARCHAR},#{item.userAge,jdbcType=INTEGER},#{item.type,jdbcType=INTEGER},#{item.createTime,jdbcType=TIMESTAMP},#{item.updateTime,jdbcType=TIMESTAMP})</foreach> </insert>測試結果
| 1000 | 1469ms |
| 2000 | 2534ms |
| 3000 | 2613ms |
| 4000 | 3549ms |
| 5000 | 4733ms |
| 8000 | 5761ms |
| 10000 | 6055ms |
2.方式二
批量新增或更新方式
注:需要給唯一主鍵添加唯一索引,update才會生效
測試結果
| 1000 | 1692ms |
| 2000 | 2346ms |
| 3000 | 3249ms |
| 4000 | 3443ms |
| 5000 | 3999ms |
| 8000 | 6460ms |
| 10000 | 7053ms |
3.方式三
單條sql+批量方式的SqlSession
<insert id="insert" >INSERT INTO lp_user_test_batch(id,user_id,user_name,user_age,type,create_time,update_time)values(#{id,jdbcType=BIGINT},#{userId,jdbcType=VARCHAR},#{userName,jdbcType=VARCHAR},#{userAge,jdbcType=INTEGER},#{type,jdbcType=INTEGER},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP}) </insert> @Resource(name = "sqlSessionFactory") private SqlSessionFactory sqlSessionFactory;/*** 利用 MyBatis 批處理特性,批量提交*/ public void batchInsert(List<UserTestBatchDO> testBatchDAOList) {//集合非空if (CollectionUtils.isEmpty(testBatchDAOList)) {return;}//批處理方式 SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);//獲得對應的MapperUserTestBatchDOMapper userTestBatchDOMapper = sqlSession.getMapper(UserTestBatchDOMapper.class);try {for (UserTestBatchDO testBatchDO : testBatchDAOList) {userTestBatchDOMapper.insert(testBatchDO);}//統一提交sqlSession.commit();} catch (Exception e) {//沒有提交的數據可以回滾sqlSession.rollback();} finally {//關閉 sqlSessionsqlSession.close();} }測試結果
| 1000 | 2174ms |
| 2000 | 3104ms |
| 3000 | 3801ms |
| 4000 | 4991ms |
| 5000 | 5930ms |
| 8000 | 8151ms |
| 10000 | 8252ms |
批量修改
1.方式一
批量新增或更新方式
注:需要給唯一主鍵添加唯一索引,update才會生效
測試結果
注:當前表內數據行數 10000
| 1000 | 1505ms |
| 2000 | 2617ms |
| 3000 | 2922ms |
| 4000 | 3292ms |
| 5000 | 3443ms |
| 8000 | 4832ms |
| 10000 | 4886ms |
優點:速度快
缺點:使用特殊語法 on duplicate key update 語法 增加sql難度性
2.方式二
單條sql+批量方式的SqlSession
<update id="updateByUserId" >UPDATE lp_user_test_batchSETuser_name = #{userName,jdbcType=VARCHAR},user_age = #{userAge,jdbcType=INTEGER},type = #{type,jdbcType=INTEGER},update_time = #{updateTime,jdbcType=TIMESTAMP}WHERE user_id = #{userId,jdbcType=VARCHAR} </update> @Resource(name = "sqlSessionFactory") private SqlSessionFactory sqlSessionFactory;/*** 利用 MyBatis 批處理特性,批量更新*/ public void batchUpdate(List<UserTestBatchDO> testBatchDAOList) {//集合非空if (CollectionUtils.isEmpty(testBatchDAOList)) {return;}//批處理方式 SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);//獲得對應的MapperUserTestBatchDOMapper userTestBatchDOMapper = sqlSession.getMapper(UserTestBatchDOMapper.class);try {for (UserTestBatchDO testBatchDO : testBatchDAOList) {userTestBatchDOMapper.updateByUserId(testBatchDO);}//統一提交sqlSession.commit();//清理緩存,防止溢出sqlSession.clearCache();} catch (Exception e) {//沒有提交的數據可以回滾sqlSession.rollback();} finally {//關閉 sqlSessionsqlSession.close();} }測試結果
注:當前表內數據行數 10000
| 1000 | 3158ms |
| 2000 | 4324ms |
| 3000 | 6466ms |
| 4000 | 7572ms |
| 5000 | 9812ms |
| 8000 | 12846ms |
| 10000 | 16088ms |
優點:通過日志觀察,生成一條執行語句sql ,多行參數,統一commit
缺點:比方式一速度略慢
3.方式三
java程序循環調用單條修改語句
執行方式:一條sql ,程序循環執行
測試結果
注:當前表內數據行數 10000
| 1000 | 33907ms |
| 2000 | 42866ms |
| 3000 | 89675ms |
| 5000 | ?104833ms |
優點:方便單條控制提交事物
缺點:耗時,耗性能、每一次循環都需要與數據庫交互一次
4.方式四
Mybatis foreach 循環
執行方式:拼接好一條sql,后執行
測試結果
注:當前表內數據行數 10000
| 1000 | 2671ms |
| 2000 | ?4170ms |
| 3000 | 4514ms |
| 4000 | 5152ms |
| 5000 | ?6572ms |
| 8000 | 10209ms |
| 10000 | 12158ms |
優點:生成多條sql,統一執行,與數據庫交互次數少
缺點 : 生成多條拼接的update語句,update語句比較多,量大了就有可能造成sql阻塞。
5.方式五
mybatis sql 使用 case when
<!-- 批量更新第二種方法,通過 case when語句變相的進行批量更新 --><update id="updateCaseByUserId" parameterType="java.util.List">update lp_user_test_batch<trim prefix="set" suffixOverrides=","><!-- 拼接case when 這是另一種寫法 --><trim prefix="user_name =case" suffix="end,"><foreach collection="list" item="item"><if test="item.userName!=null">when user_id = #{item.userId,jdbcType=VARCHAR} then #{item.userName,jdbcType=VARCHAR}</if></foreach></trim><trim prefix="user_age =case" suffix="end,"><foreach collection="list" item="item"><if test="item.userAge!=null">when user_id = #{item.userId,jdbcType=VARCHAR} then #{item.userAge,jdbcType=INTEGER}</if></foreach></trim><trim prefix="type =case" suffix="end,"><foreach collection="list" item="item"><if test="item.type!=null">when user_id = #{item.userId,jdbcType=VARCHAR} then #{item.type,jdbcType=INTEGER}</if></foreach></trim><trim prefix="update_time =case" suffix="end,"><foreach collection="list" item="item"><if test="item.type!=null">when user_id = #{item.userId,jdbcType=VARCHAR} then #{item.updateTime,jdbcType=TIMESTAMP}</if></foreach></trim></trim><where>user_id in<foreach collection="list" index="index" item="item" separator="," open="(" close=")">#{item.userId,jdbcType=VARCHAR}</foreach></where></update>測試結果
注:當前表內數據行數 10000
| 1000 | 3201ms |
| 2000 | 4804ms |
| 3000 | 6833ms |
| 4000 | 8554ms |
| 5000 | 11688ms |
| 8000 | 26501ms |
| 10000 | 34724ms |
缺點:
xml中的循環體有點多,每一個case when 都要循環一遍list集合,所以大批量拼sql的時候會比較慢。
生成多條拼接sql,sql長度過長,容易sql超長引起報錯 Packet for query is too large。
MySQL 最大允許的 packet
Mybatis批處理介紹
Mybatis內置執行器類型ExecutorType有3種
分別是
ExecutorType.SIMPLE: 不做特殊處理,為每個語句的執行創建一個新的預處理語句。
ExecutorType.REUSE: 可以復用預處理語句。
ExecutorType.BATCH:可以批量執行所有更新語句
SIMPLE與BATCH(批量)對比
默認的是simple,該模式下它為每個語句的執行創建一個新的預處理語句,單條提交sql;
而batch模式重復使用已經預處理的語句,并且批量執行所有更新語句,顯然batch性能將更優;但是批量模式無法返回自增主鍵
測試環境配置
系統:win 8.1
Mysql : 5.7
java環境:junit
注:環境不同可能會引起耗時存在差異。
總結
單次批量操作不要過大,批量新增使用方式一,批量更新方式一與方式二經過測試是最優的選擇
也可以根據安全方面綜合考慮,選擇適合的方式。
關注程序員小強公眾號更多編程趣事,知識心得與您分享
總結
以上是生活随笔為你收集整理的Mybatis 批量操作8种实现总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache shiro反序列化(CVE
- 下一篇: Unity虚拟相机CinemaChine