您现在的位置是:首页 > 编程语言学习 > 后端编程语言 > 文章正文 后端编程语言

使用Mybatis的Batch Insert Support 实现批量插入

2022-07-11 10:17:00 后端编程语言

简介这篇文章主要介绍了使用Mybatis的Batch Insert Support 实现批量插入。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全...

这篇文章主要介绍了使用Mybatis的Batch Insert Support 实现批量插入。具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教!

Batch Insert Support 批量插入

在开发中如果遇到需要批量insert的需求,可以使用Mybatis 的 Batch Insert Support 提高插入效率。

代码实例(开发的项目中截取的片段)

  1. @Autowired 
  2. private SqlSessionTemplate sqlSessionTemplate; 
  3. public int insertFolder(List<IpsCatalogFolderDetail> ips) { 
  4.         //获取sql会话 
  5.         SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false); 
  6.         //通过新的session获取mapper,而不是常规的spring管理注入 
  7.         IipsCatalogFolderDetailDao folderDetailDao = session.getMapper(IipsCatalogFolderDetailDao.class); 
  8.         int size = ips.size(); 
  9.         //如果有父类子类两层都需要批量插入也可 
  10.         try { 
  11.         //外层循环 
  12.             for (int i = 0; i < size; i++) { 
  13.                     ips.get(i).setType("folder"); 
  14.                     //用上面在session中获取的mapper进行插入操作 
  15.                     folderDetailDao.insertFolder(ips.get(i)); 
  16.                 //内层循环 
  17.                 String cs = ips.get(i).getContentIds(); 
  18.                 if (StringUtils.isNotBlank(cs)){ 
  19.                         List<String> con = JSON.parseArray(cs,String.class); 
  20.                     if (cs != null && con.size() > 0) { 
  21.                         for (int j = 0; j < con.size(); j++) { 
  22.                                 IpsCatalogFolderDetail ifd = new IpsCatalogFolderDetail(); 
  23.                                 ifd.setParentCode(ips.get(i).getCode()); 
  24.                                 ifd.setContentId(con.get(j)); 
  25.                                 ifd.setType("contents"); 
  26.                                  //同样用上面在session中获取的mapper进行插入操作 
  27.                                 folderDetailDao.insertFolder(ifd); 
  28.                         } 
  29.                     } 
  30.                 } 
  31.                 //最后批量提交 
  32.                     if (i % 200 == 0 || i == size - 1) { 
  33.                         session.commit();//200个提交一次,手动提交,提交后无法回滚 
  34.                         session.clearCache(); //清理缓存,防止溢出 
  35.                     } 
  36.             } 
  37.         }catch (Exception e) { 
  38.             System.out.println(e.toString()); 
  39.            session.rollback(); //没有提交的数据可以回滚 
  40.         } finally { 
  41.             session.close(); 
  42.         } 
  43.         return 0; 
  44.     } 

另外有时我们在插入的时候需要先查询数据是否已存在,如果也需要批量操作可将insert和update语句合并,然后就可以继续使用Batch Insert了

ORACLE数据库sql示例

  1. @Insert("merge into ips_catalog_folder_detail fd " + 
  2.             "using(select #{code,jdbcType=VARCHAR} c from dual)t " + 
  3.             "on(fd.FOLDERID = t.c)" + 
  4.              
  5.             "when matched then"
  6.             "update set "
  7.             ...(省略)... 
  8.             "where ..."
  9.             "when not matched then insert(" + 
  10.             "fd.PROD_LINE," + 
  11.             "fd.TYPE," + 
  12.             "fd.PARENTFOLDERCODE," + 
  13.             "fd.FOLDERID," + 
  14.             "fd.FOLDERCODE," + 
  15.             "fd.FOLDERNAME," + 
  16.             "fd.COLUMN_SORTINDEX," + 
  17.             "fd.DESCRIPTION," + 
  18.             "fd.CONTENTID," + 
  19.             "fd.CREATETIME" + 
  20.             ")" + 
  21.             "VALUES" + 
  22.             "(" + 
  23.             "#{prod_line}," + 
  24.             "#{type,jdbcType=VARCHAR}," + 
  25.             "#{parentCode,jdbcType=VARCHAR}," + 
  26.             "#{code,jdbcType=VARCHAR}," + 
  27.             "#{aliasCode,jdbcType=VARCHAR}," + 
  28.             "#{name,jdbcType=VARCHAR}," + 
  29.             "#{sortIndex,jdbcType=VARCHAR}," + 
  30.             "#{desc,jdbcType=VARCHAR}," + 
  31.             "#{contentId,jdbcType=VARCHAR}," + 
  32.             "#{createTime,jdbcType=VARCHAR}" + 
  33.             ")"
  34.             int insertFolder(IpsCatalogFolderDetail fd); 

MYSQL示例:

  1. REPLACE INTO users (id,name,age) VALUES(1, '张雨绮', 32);  

批量插入几千条数据优化(foreach)

项目中有一个耗时较长的Job存在CPU占用过高的问题

经排查发现,主要时间消耗在往MyBatis中批量插入数据。mapper configuration是用foreach循环做的,差不多是这样。

  1. <insert id="batchInsert" parameterType="java.util.List"
  2.     insert into USER (id, name) values 
  3.     <foreach collection="list" item="model" index="index" separator=",">  
  4.         (#{model.id}, #{model.name}) 
  5.     </foreach> 
  6. </insert> 

优化代码

可以看 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容)

  1. SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); 
  2. try { 
  3.     SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class); 
  4.     List<SimpleTableRecord> records = getRecordsToInsert(); // not shown 
  5.   
  6.     BatchInsert<SimpleTableRecord> batchInsert = insert(records) 
  7.             .into(simpleTable) 
  8.             .map(id).toProperty("id"
  9.             .map(firstName).toProperty("firstName"
  10.             .map(lastName).toProperty("lastName"
  11.             .map(birthDate).toProperty("birthDate"
  12.             .map(employed).toProperty("employed"
  13.             .map(occupation).toProperty("occupation"
  14.             .build() 
  15.             .render(RenderingStrategy.MYBATIS3); 
  16.   
  17.     batchInsert.insertStatements().stream().forEach(mapper::insert); 
  18.     session.commit(); 
  19. finally { 
  20.     session.close(); 

总结一下,如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用 <foreach>的插入的话,需要将每次插入的记录控制在 20~50 左右

相关文章

站点信息