日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MyBastis 三种批量插入方式的性能比较

發布時間:2023/12/13 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MyBastis 三种批量插入方式的性能比较 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

數據庫使用的是MySQL,JDK版本1.8,運行在SpringBoot環境下

本文章源代碼:https://github.com/runbeyondmove/mybatis-batch-demo

對比3種可用的方式

1、反復執行單條插入語句
2、xml拼接sql
3、批處理執行

先說結論:少量插入請使用反復插入單條數據,方便。數量較多請使用批處理方式。(可以考慮以有需求的插入數據量20條左右為界吧,在我的測試和數據庫環境下耗時都是百毫秒級的,方便最重要)。無論何時都不用xml拼接sql的方式

1. xml映射文件中的代碼

<insert id="insert" parameterType="top.spanrun.bootssm.model.UserInf" useGeneratedKeys="true" keyProperty="id"><!--@mbggenerated generator自動生成,注意order的before和after--><!--<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer">SELECT LAST_INSERT_ID()</selectKey>-->insert into user_inf (id, uname, passwd, gentle, email, city)values (#{id,jdbcType=INTEGER}, #{uname,jdbcType=VARCHAR}, #{passwd,jdbcType=VARCHAR}, #{gentle,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{city,jdbcType=VARCHAR})</insert><insert id="insertWithXML" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">insert into user_inf (id, uname, passwd, gentle, email, city)values<foreach collection="list" item="user" index="index" separator=",">(#{user.id,jdbcType=INTEGER}, #{user.uname,jdbcType=VARCHAR}, #{user.passwd,jdbcType=VARCHAR},#{user.gentle,jdbcType=VARCHAR}, #{user.email,jdbcType=VARCHAR}, #{user.city,jdbcType=VARCHAR})</foreach></insert>

?

2.?Mapper接口

@Mapper public interface UserInfMapper {int insert(UserInf record);int insertWithXML(@Param("list") List<UserInf> list); }

?

3. Service實現,接口聲明省略

@Service public class UserInfServiceImpl implements UserInfService{private static final Logger LOGGER = LoggerFactory.getLogger(UserInfServiceImpl.class);@AutowiredSqlSessionFactory sqlSessionFactory;@AutowiredUserInfMapper userInfMapper;@Transactional@Overridepublic boolean testInsertWithBatch(List<UserInf> list) {LOGGER.info(">>>>>>>>>>>testInsertWithBatch start<<<<<<<<<<<<<<");SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH,false);UserInfMapper mapper = sqlSession.getMapper(UserInfMapper.class);long startTime = System.nanoTime();try {List<UserInf> userInfs = Lists.newArrayList();for (int i = 0; i < list.size(); i++) {// 每1000條提交一次
if ((i+1)%1000 == 0){sqlSession.commit();sqlSession.clearCache();}mapper.insert(list.get(i));}} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();}LOGGER.info("testInsertWithBatch spend time:{}",System.nanoTime()-startTime);LOGGER.info(">>>>>>>>>>>testInsertWithBatch end<<<<<<<<<<<<<<");return true;}@Transactional@Overridepublic boolean testInsertWithXml(List<UserInf> list) {LOGGER.info(">>>>>>>>>>>testInsertWithXml start<<<<<<<<<<<<<<");long startTime = System.nanoTime();userInfMapper.insertWithXML(list);LOGGER.info("testInsertWithXml spend time:{}",System.nanoTime()-startTime);LOGGER.info(">>>>>>>>>>>testInsertWithXml end<<<<<<<<<<<<<<");return true;}@Transactional@Overridepublic boolean testInsertWithForeach(List<UserInf> list) {LOGGER.info(">>>>>>>>>>>testInsertWithForeach start<<<<<<<<<<<<<<");long startTime = System.nanoTime();for (int i = 0; i < list.size(); i++) {userInfMapper.insert(list.get(i));}LOGGER.info("testInsertWithForeach spend time:{}",System.nanoTime()-startTime);LOGGER.info(">>>>>>>>>>>testInsertWithForeach end<<<<<<<<<<<<<<");return true;}@Transactional@Overridepublic boolean testInsert(UserInf userInf) {LOGGER.info(">>>>>>>>>>>testInsert start<<<<<<<<<<<<<<");long startTime = System.nanoTime();LOGGER.info("insert before,id=" + userInf.getId());userInfMapper.insert(userInf);LOGGER.info("insert after,id=" + userInf.getId());LOGGER.info("testInsert spend time:{}",System.nanoTime()-startTime);LOGGER.info(">>>>>>>>>>>testInsert end<<<<<<<<<<<<<<");return true;} }

 

4.?Controller控制器

@RestController public class UserInfController {@AutowiredUserInfService userInfService;@RequestMapping(value = "test/{size}/{type}")public void testInsert(@PathVariable(value = "size") Integer size,@PathVariable(value = "type") Integer type){System.out.println(">>>>>>>>>>>>type = " + type + "<<<<<<<<<<<<<");switch (type){case 1:userInfService.testInsertWithForeach(generateList(size));break;case 2:userInfService.testInsertWithXml(generateList(size));break;case 3:userInfService.testInsertWithBatch(generateList(size));break;default:UserInf userInf = new UserInf();userInf.setUname("user_single");userInf.setGentle("1");userInf.setEmail("123@123.com");userInf.setCity("廣州市");userInf.setPasswd("123456");userInfService.testInsert(userInf);}}private List<UserInf> generateList(int listSize){List<UserInf> list = Lists.newArrayList();UserInf userInf = null;for (int i = 0; i < listSize; i++) {userInf = new UserInf();userInf.setUname("user_" + i);userInf.setGentle("1");userInf.setEmail("123@123.com");userInf.setCity("廣州市");userInf.setPasswd("123456");list.add(userInf);}return list;} }

  

測試結果(單位是納秒):

1000 testInsertWithForeach spend time:431526521 testInsertWithXml spend time:118772867 testInsertWithBatch spend time:17560234610000 testInsertWithForeach spend time:2072525050 testInsertWithXml spend time:685605121 testInsertWithBatch spend time:894647254100000 testInsertWithForeach spend time:18950160161 testInsertWithBatch spend time:8469312537testInsertWithXml報錯 ### Cause: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (9388970 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable. ; Packet for query is too large (9388970 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.; nested exception is com.mysql.jdbc.PacketTooBigException: Packet for query is too large
(9388970 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.] with root causecom.mysql.jdbc.PacketTooBigException: Packet for query is too large (9388970 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.

  查看xml?sql拼接的異常信息,可以發現,最大只能達到4194304,也就是4M,所以這種方式不推薦

結論

循環插入單條數據雖然效率極低,但是代碼量極少,如果在使用tk.Mapper的插件情況下,僅需代碼,:

@Transactional public void add1(List<Item> itemList) {itemList.forEach(itemMapper::insertSelective); }

因此,在需求插入數據數量不多的情況下肯定用它了。

xml拼接sql是最不推薦的方式,使用時有大段的xml和sql語句要寫,很容易出錯,工作效率很低。更關鍵點是,雖然效率尚可,但是真正需要效率的時候你掛了,要你何用?

批處理執行是有大數據量插入時推薦的做法,使用起來也比較方便。

?

其他在使用中的補充:

1.?使用mybatis?generator生成器生成中的一些坑

代碼說明:數據庫是MySQL,且主鍵自增,用generator 生成的mapper.xml中的代碼,自增ID,使用的是selectKey來獲取。

問題描述:insert的時候,添加的時候,第一條數據添加成功,接著添加第二條數據的時候會提示失敗,失敗的原因是ID還是使用的上一個ID值,主鍵重復導致插入失敗。異常如下:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '4' for key 'PRIMARY'

問題原因:BEFORE還是AFTER的問題

<selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Integer">SELECT LAST_INSERT_ID() </selectKey>

需要注意的是,Oracle使用before,MySQL使用after

其實在使用Mybatis?generator生成帶代碼的時候可以通過identity="true"來指定生成的selectKey是before還是after

<generatedKey column="id" sqlStatement="Mysql" identity="true" />

注:在select標簽中使用useGeneratedKeys="true"?keyProperty="id" 不存在該問題

?

2. mybatis的版本

升級Mybatis版本到3.3.1

?

3.?在批量插入的拼接xml?sql時注意foreach是沒有使用open和close的,但是在批量查詢修改刪除時才使用到open和close

<foreach collection="list" item="user" index="index" separator=",">(#{user.id,jdbcType=INTEGER}, #{user.uname,jdbcType=VARCHAR}, #{user.passwd,jdbcType=VARCHAR},#{user.gentle,jdbcType=VARCHAR}, #{user.email,jdbcType=VARCHAR}, #{user.city,jdbcType=VARCHAR})</foreach>

  

?4. 使用批量提交注意的事項

  a. 事務

    由于在 Spring 集成的情況下,事務連接由 Spring 管理(SpringManagedTransaction),所以這里不需要手動關閉?sqlSession,在這里手動提交(commit)或者回滾(rollback)也是無效的。

?  b. 批量提交

    批量提交只能應用于 insert, update, delete。

    并且在批量提交使用時,如果在操作同一SQL時中間插入了其他數據庫操作,就會讓批量提交方式變成普通的執行方式,所以在使用批量提交時,要控制好 SQL 執行順序

?

轉載于:https://www.cnblogs.com/move22/p/9811726.html

總結

以上是生活随笔為你收集整理的MyBastis 三种批量插入方式的性能比较的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。