往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