Mybatis實現(xiàn)批量操作8種小結(jié)
批量新增
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>
測試結(jié)果
數(shù)量 | 耗時 |
---|---|
1000 | 1469ms |
2000 | 2534ms |
3000 | 2613ms |
4000 | 3549ms |
5000 | 4733ms |
8000 | 5761ms |
10000 | 6055ms |
2.方式二
批量新增或更新方式
注:需要給唯一主鍵添加唯一索引,update才會生效
<!-- 批量新增或更新--> <insert id="batchSaveOrUpdate" 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> ON DUPLICATE KEY UPDATE user_name = VALUES(user_name), user_age = VALUES(user_age), type = VALUES(type), update_time = VALUES(update_time) </insert>
測試結(jié)果
數(shù)量 | 耗時 |
---|---|
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; } //批處理方式 SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); //獲得對應的Mapper UserTestBatchDOMapper userTestBatchDOMapper = sqlSession.getMapper(UserTestBatchDOMapper.class); try { for (UserTestBatchDO testBatchDO : testBatchDAOList) { userTestBatchDOMapper.insert(testBatchDO); } //統(tǒng)一提交 sqlSession.commit(); } catch (Exception e) { //沒有提交的數(shù)據(jù)可以回滾 sqlSession.rollback(); } finally { //關閉 sqlSession sqlSession.close(); } }
測試結(jié)果
數(shù)量 | 耗時 |
---|---|
1000 | 2174ms |
2000 | 3104ms |
3000 | 3801ms |
4000 | 4991ms |
5000 | 5930ms |
8000 | 8151ms |
10000 | 8252ms |
批量修改
1.方式一
批量新增或更新方式
注:需要給唯一主鍵添加唯一索引,update才會生效
<!-- 批量新增或更新--> <insert id="batchSaveOrUpdate" 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> ON DUPLICATE KEY UPDATE user_name = VALUES(user_name), user_age = VALUES(user_age), type = VALUES(type), update_time = VALUES(update_time) </insert>
測試結(jié)果
注:當前表內(nèi)數(shù)據(jù)行數(shù) 10000
數(shù)量 | 耗時 |
---|---|
1000 | 1505ms |
2000 | 2617ms |
3000 | 2922ms |
4000 | 3292ms |
5000 | 3443ms |
8000 | 4832ms |
10000 | 4886ms |
優(yōu)點:速度快
缺點:使用特殊語法 on duplicate key update 語法 增加sql難度性
2.方式二
單條sql+批量方式的SqlSession
<update id="updateByUserId" > UPDATE lp_user_test_batch SET user_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; } //批處理方式 SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); //獲得對應的Mapper UserTestBatchDOMapper userTestBatchDOMapper = sqlSession.getMapper(UserTestBatchDOMapper.class); try { for (UserTestBatchDO testBatchDO : testBatchDAOList) { userTestBatchDOMapper.updateByUserId(testBatchDO); } //統(tǒng)一提交 sqlSession.commit(); //清理緩存,防止溢出 sqlSession.clearCache(); } catch (Exception e) { //沒有提交的數(shù)據(jù)可以回滾 sqlSession.rollback(); } finally { //關閉 sqlSession sqlSession.close(); } }
測試結(jié)果
注:當前表內(nèi)數(shù)據(jù)行數(shù) 10000
數(shù)量 | 耗時 |
---|---|
1000 | 3158ms |
2000 | 4324ms |
3000 | 6466ms |
4000 | 7572ms |
5000 | 9812ms |
8000 | 12846ms |
10000 | 16088ms |
優(yōu)點:通過日志觀察,生成一條執(zhí)行語句sql ,多行參數(shù),統(tǒng)一commit
缺點:比方式一速度略慢
3.方式三
java程序循環(huán)調(diào)用單條修改語句
執(zhí)行方式:一條sql ,程序循環(huán)執(zhí)行
for (UserTestBatchDO userTestBatch : testBatchDAOList) { userTestBatchDOMapper.updateByUserId(userTestBatch); }
測試結(jié)果
注:當前表內(nèi)數(shù)據(jù)行數(shù) 10000
數(shù)量 | 耗時 |
---|---|
1000 | 33907ms |
2000 | 42866ms |
3000 | 89675ms |
5000 | ?104833ms |
優(yōu)點:方便單條控制提交事物
缺點:耗時,耗性能、每一次循環(huán)都需要與數(shù)據(jù)庫交互一次
4.方式四
Mybatis foreach 循環(huán)
執(zhí)行方式:拼接好一條sql,后執(zhí)行
<!-- 接收list參數(shù),循環(huán)著組裝sql語句,注意for循環(huán)的寫法 separator=";" 代表著每次循環(huán)完,在sql后面放一個分號 --> <update id="updateForeachByUserId" parameterType="java.util.List"> <foreach collection="list" item="item" separator=";"> UPDATE lp_user_test_batch SET user_name = #{item.userName,jdbcType=VARCHAR}, user_age = #{item.userAge,jdbcType=INTEGER}, type = #{item.type,jdbcType=INTEGER}, update_time = #{item.updateTime,jdbcType=TIMESTAMP} WHERE user_id = #{item.userId,jdbcType=VARCHAR} </foreach> </update>
測試結(jié)果
注:當前表內(nèi)數(shù)據(jù)行數(shù) 10000
數(shù)量 | 耗時 |
---|---|
1000 | 2671ms |
2000 | ?4170ms |
3000 | 4514ms |
4000 | 5152ms |
5000 | ?6572ms |
8000 | 10209ms |
10000 | 12158ms |
優(yōu)點:生成多條sql,統(tǒng)一執(zhí)行,與數(shù)據(jù)庫交互次數(shù)少
缺點 : 生成多條拼接的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>
測試結(jié)果
注:當前表內(nèi)數(shù)據(jù)行數(shù) 10000
數(shù)量 | 耗時 |
---|---|
1000 | 3201ms |
2000 | 4804ms |
3000 | 6833ms |
4000 | 8554ms |
5000 | 11688ms |
8000 | 26501ms |
10000 | 34724ms |
缺點:
xml中的循環(huán)體有點多,每一個case when 都要循環(huán)一遍list集合,所以大批量拼sql的時候會比較慢。
生成多條拼接sql,sql長度過長,容易sql超長引起報錯 Packet for query is too large。
MySQL 最大允許的 packet
Mybatis批處理介紹
Mybatis內(nèi)置執(zhí)行器類型ExecutorType有3種
分別是
ExecutorType.SIMPLE: 不做特殊處理,為每個語句的執(zhí)行創(chuàng)建一個新的預處理語句。
ExecutorType.REUSE: 可以復用預處理語句。
ExecutorType.BATCH:可以批量執(zhí)行所有更新語句
SIMPLE與BATCH(批量)對比默認的是simple,該模式下它為每個語句的執(zhí)行創(chuàng)建一個新的預處理語句,單條提交sql;
而batch模式重復使用已經(jīng)預處理的語句,并且批量執(zhí)行所有更新語句,顯然batch性能將更優(yōu);但是批量模式無法返回自增主鍵
測試環(huán)境配置
系統(tǒng):win 8.1
Mysql : 5.7
java環(huán)境:junit
注:環(huán)境不同可能會引起耗時存在差異。
總結(jié)
單次批量操作不要過大,批量新增使用方式一,批量更新方式一與方式二經(jīng)過測試是最優(yōu)的選擇
也可以根據(jù)安全方面綜合考慮,選擇適合的方式。
到此這篇關于Mybatis實現(xiàn)批量操作8種小結(jié)的文章就介紹到這了,更多相關Mybatis 批量操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java+vue3+el-tree實現(xiàn)樹形結(jié)構(gòu)操作代碼
基于springboot + vue3 elementPlus實現(xiàn)樹形結(jié)構(gòu)數(shù)據(jù)的添加、刪除和頁面展示,本文通過示例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-06-06java8 利用reduce實現(xiàn)將列表中的多個元素的屬性求和并返回操作
這篇文章主要介紹了java8 利用reduce實現(xiàn)將列表中的多個元素的屬性求和并返回操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08Spring Cloud Gateway 如何修改HTTP響應信息
這篇文章主要介紹了Spring Cloud Gateway 修改HTTP響應信息的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法
這篇文章主要介紹了詳解MyBatis-Plus updateById方法更新不了空字符串/null解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09springboot項目實現(xiàn)斷點續(xù)傳功能
這篇文章主要介紹了springboot項目實現(xiàn)斷點續(xù)傳,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08