首页 > 其他分享 >mybatis的坑-foreach

mybatis的坑-foreach

时间:2023-04-13 10:55:43浏览次数:28  
标签:语句 insert name 插入 VALUES user mybatis foreach

往MyBatis中批量插入数据,我们常常这么干

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

这个方法提升批量插入速度的原理是,将传统的:

INSERT INTO `user` (`name`, `age`) VALUES ("zs", "10");  
INSERT INTO `user` (`name`, `age`) VALUES ("ls", "12");  
INSERT INTO `user` (`name`, `age`) VALUES ("ww", "14");  
INSERT INTO `user` (`name`, `age`) VALUES ("hl", "11");  
INSERT INTO `user` (`name`, `age`) VALUES ("sw", "54");

转化为:

INSERT INTO `user` (`name`, `age`)   
      VALUES ("zs", "10"),  
             ("ls", "12"),  
             ("ww", "14"),  
             ("hl", "11"),  
             ("sw", "54"); 

  理想情况下,如果要优化插入速度时,可以将许多小型操作组合到一个大型操作中,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。

  乍看上去这个foreach没有问题,但是经过项目实践发现,当表的列数较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,这是不能忍的。

  当插入数量很多时,不能一次性全放在一条语句里。因为默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象。当我们不停地使用这个批量插入方法,而MyBatis对于含有<foreach>的语句,无法采用缓存,那么在每次调用方法时,都会重新解析sql语句。

  如果我们的foreach后有5000+个values,那么这个PreparedStatement特别长,他包含了很多占位符,对于占位符和参数的映射尤其耗时。查阅相关资料可知,values的增长与所需的解析时间,是呈指数型增长的。

  所以,如果非要使用 foreach 的方式来进行批量插入的话,可以考虑减少一条 insert 语句中 values 的个数,最好能达到上面曲线的最底部的值,使速度最快。一般按经验来说,一次性插20~50行数量是比较合适的,时间消耗也能接受。

  而提升性能的方式,MyBatis文档中推荐使用另外一种方法写批量插入。(可以看 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容)

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);  
try {  
    SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);  
    List<SimpleTableRecord> records = getRecordsToInsert(); // not shown  
   
    BatchInsert<SimpleTableRecord> batchInsert = insert(records)  
            .into(simpleTable)  
            .map(id).toProperty("id")  
            .map(firstName).toProperty("firstName")  
            .map(lastName).toProperty("lastName")  
            .map(birthDate).toProperty("birthDate")  
            .map(employed).toProperty("employed")  
            .map(occupation).toProperty("occupation")  
            .build()  
            .render(RenderingStrategy.MYBATIS3);  
   
    batchInsert.insertStatements().stream().forEach(mapper::insert);  
   
    session.commit();  
} finally {  
    session.close();  
}  

  基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后多次执行插入语句。就类似于JDBC的下面语句一样。

Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");  
connection.setAutoCommit(false);  
PreparedStatement ps = connection.prepareStatement(  
        "insert into tb_user (name) values(?)");  
for (int i = 0; i < stuNum; i++) {  
    ps.setString(1,name);  
    ps.addBatch();  
}  
ps.executeBatch();  
connection.commit();  
connection.close(); 

  使用 ExecutorType.BATCH 的插入方式后,性能会显著提升。如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用 <foreach> 的插入的话,需要将每次插入的记录控制在 20~50 左右。

 

标签:语句,insert,name,插入,VALUES,user,mybatis,foreach
From: https://www.cnblogs.com/jacking0325/p/17312651.html

相关文章

  • mybatisPlus-增删改查
    引入依赖<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.or......
  • Mybatis-Plus如何自定义SQL注入器?
    有关Mybatis-Plus常用功能之前有做过一篇总结:MyBatisPlus常用功能总结!(附项目示例)一、什么是SQL注入器我们在使用Mybatis-Plus时,dao层都会去继承BaseMapper接口,这样就可以用BaseMapper接口所有的方法,BaseMapper中每一个方法其实就是一个SQL注入器在Mybatis-Plus的核心(core......
  • Mybatis-关联关系映射
    1.一对多 1.1.导入数据表--一对多--客户表(主表)createtablet_customer(customer_idintprimarykeynotnullauto_increment,customer_namevarchar(50)notnull);--多对一--订单表(从表)createtablet_order(order_idintprimarykeynotnull......
  • Mybatis中 ${} 和 #{} 的用法与区别
    #{}:将传入的数据都当成一个字符串,会对传入的变量自动加一个单引号。如:user_id=#{userId},如果传入的值是111,那么解析成sql时的值为user_id='111',如果传入的值是id,则解析成的sql为user_id='id'。${}:将传入的参数直接显示生成在sql中,且不加任何引号。如:user_id=${userId},如......
  • mybatis全局变量 (mybatis.configuration.variables) 的应用
    mybatis.configuration.variables是一个可自定义的全局变量:在application.yml中定义:mybatis:mapper-locations:classpath:mapper/*.xmltype-aliases-package:com.example.entityconfiguration:variables:dbtype:mysqlmapper.xml中的使用:<!--更新......
  • mybatis.type-aliases-package 的作用
    在mapper.xml文件中的resultMap的type或者parameterType会用到自定义的POJO。如果在application.yml中没有配置mybatis.type-aliases-package的话,就需要写全限定类名:<selectid="selectByStudentById"resultType="com.example.domain.Student">SELECT*FROMstudent......
  • MyBatis
    1.简介MyBatis是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和JavaPOJO(PlainOldJavaObjects,普通老式Java对象)为数......
  • web前端tips:使用 forEach 循环中的 return 语句会发生什么?
    近日,笔者在认真搬砖的过程中,突然遇到一个问题,请看大屏幕(代码):data(){return{statusList:[{code:"1",name:"已保存"},{code:"2",name:"已提交"}]......
  • MyBatis中XXMapper示例记录
    XXMapper.xml的结构示例如下,包括<resultMap>、<id>、<result>、<select>、<update>、<foreach>、<if>标签的使用:<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTDMapper......
  • 【随手记】解决mybatis返回List<map>类型的数据时 无序 并且 不能返回空值
    返回结果无序希望表格的列能根据数据库查出来的数据保持一致,但是返回页面的结果集是无序在mybatis中使用List<Map>结构接收数据,发现输入的sql语句结果并不是按照输入的字段名顺序返回的。例如输入selectcol1,col2,col3fromtable却返回col2col3col1***......