点击上方名片关注我,为你带来更多踩坑案例
- 引言 -
如果你是一个摸爬滚打几年的开发者,那么这个阶段,对系统设计的合理性绝对是衡量一个人水平的重要标准。
一个好的设计不光能让你工作中避免很多麻烦,还能为你面试的时候增加很多谈资
而且,不同设计之间理念都是有借鉴性参考性的,你见过的设计多了,思考的多了,再次面临一个问题的时候,就会有很多点子不由自主的冒出来。
希望这个系列的文章,能够和大家互相借鉴参考,共同进步。
- 问题背景 -
最近在做一个解析文本内容功能的时候,遇到了事务执行时间太长的问题,问题的具体分析可以见
总而言之就是一个大批量、长json的解析,要往库中插入上千甚至上万条的关联数据。
- 问题分析 -
首先肯定不能一条一条的插入了,都这个量级了,必须来批量插入(不能偷懒了)。
批量插入,要么就是自己写sql拼参数,要么就是使用持久层框架现成的一些封装方法。
- JPA的批量插入 -
@Override
public Integer batchInsertFile(List<File> fileList) {
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO test_file " +
"(project_id, fid, name, size, use_type, path, absolute_path, resolution, subfix, file_id, inner_location, create_uid, status, valid_status) " +
"VALUES ");
for (int i = 0; i < fileList.size(); i++) {
sql.append(" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
if (i != fileList.size() - 1) {
sql.append(",");
}
}
sql.append("on DUPLICATE key update project_id = VALUES(assets_id), name = VALUES(name), size = VALUES(size), use_type = VALUES(use_type)" +
", path = VALUES(path), absolute_path = VALUES(absolute_path), resolution = VALUES(resolution), subfix = VALUES(subfix), STATUS = VALUES(STATUS), valid_status = VALUES(valid_status)");
Query query = entityManager.createNativeQuery(sql.toString());
int paramIndex = 1;
for (File file : fileList) {
query.setParameter(paramIndex++, file.getProjectId());
query.setParameter(paramIndex++, file.getFid());
query.setParameter(paramIndex++, file.getName());
query.setParameter(paramIndex++, file.getSize());
query.setParameter(paramIndex++, file.getUseType());
query.setParameter(paramIndex++, file.getPath());
query.setParameter(paramIndex++, file.getAbsolutePath());
query.setParameter(paramIndex++, file.getResolution());
query.setParameter(paramIndex++, file.getSubfix());
query.setParameter(paramIndex++, file.getFileId());
query.setParameter(paramIndex++, file.getInnerLocation());
query.setParameter(paramIndex++, file.getCreateUid());
query.setParameter(paramIndex++, file.getStatus());
query.setParameter(paramIndex++, file.getValidStatus());
}
return query.executeUpdate();
}
类似这样的方式,可以自己拼上唯一索引来处理插入重复的一些逻辑,或者使用insert ingore直接忽略。
但是前面也说了,我批量插入的数据是往好几个表中插入,有层级关联关系的,上述这种方式拿不到插入的id,插入再查一次的话感觉有点脱了裤子放屁,不优雅。
- mybatis的批量插入 -
找了一下,发现mybatis中支持批量插入后返回实体id,首先项目中引入mybatis
yml文件加上
启动类加上
批量插入的方法
配置bean
然后就是写具体的方法了
最后就可以直接把封装好的批量数据插入了。
使用这个方法的话入参中的id是会回写回来的。
- 结束语 -
批量插入的方法有有很多,但是不得不说有一些方法是具有迷惑性的,会让你误以为是批量插入,但最后提交到数据库的sql语句依然是多条。
所以不管使用哪种方法,我们最后还是要确认下,最后执行的sql是一条insert还是多条,这是最根本的点。