Mybatis批量插入Oracle数据的方法实例
作者:night_field 发布时间:2021-05-24 23:32:31
基本环境
语言:Java 8 数据库:Oracle ORM 框架:MyBatis 3.4.5
需求
批量插入数据,数据需要有自增 id。每次插入有一个唯一的 sessionId 来标记这些记录,插入完成之后返回这个 sessionId。
方案
循环插入单条记录,伪代码:
int sessionId = dao.querySessionId();
for (Record record : recordList) {
dao.insertRecord(record, sessionId);
}
return sessionId;
上述解决方案很简明易懂,但是循环中每次 insert 操作都会与 DB 交互一次,当数据量很大时,会花费很多时间在网络传输上,导致性能问题。
改进
问题可以通过批量插入来改善。
带自增 id 的批量插入
Oracle 中比较常见的批量插入模版是:
INSERT ALL
INTO target_table (col1, col2, col3) VALUES ('id1_1', 'val1_2', 'val1_3')
INTO target_table (col1, col2, col3) VALUES ('id2_1', 'val2_2', 'val2_3')
...
Subquery;
但是每条记录都需要有一个自增 id,Oracle 中一般用 Sequence 来实现,于是比较容易想到的是用下面这种方式:
INSERT ALL
INTO table (col1, col2, col3) VALUES (SEQUENCE.NEXTVAL, 'val1_2', 'val1_3')
INTO table (col1, col2, col3) VALUES (SEQUENCE.NEXTVAL, 'val2_2', 'val2_3')
...
SELECT 1 FROM DUAL;
不过遗憾的是,上述方案行不通,因为所有的 SEQUENCE.NEXTVAL 都会是同一个值,会产生主键冲突。
接着尝试其他的方案。Oracle 支持从一个表里批量选取数据插入另一个表中:
INSERT INTO target_table (col1, col2, col3)
SELECT col1,
col2,
col3
FROM source_table
WHERE condition;
用上述方式,我们可以把 * 入的数据用 UNION ALL
构造一个子表,也就是上面的 source_table 来实现批量插入。跟 INSERT ALL INTO
相比的好处是,可以使用 Sequence 的自增值:
INSERT INTO target_table (col1, col2, col3)
SELECT SEQUENCE.NEXTVAL,
col2,
col3
FROM (
SELECT 'val1_2' col2, 'val1_3' col3 FROM dual UNION ALL
SELECT 'val2_2' col2, 'val2_3' col3 FROM dual UNION ALL
...
);
用 MyBatis 的 dynamic sql 来实现大致如下:
<insert id="sampleInsert" parameterType="java.util.List">
INSERT INTO target_table (col1, col2, col3)
SELECT SEQUENCE.NEXTVAL,, col2, col3 FROM
<foreach collection="list" item="item" index="index" open="(" close=")" separator=" UNION ALL ">
SELECT #{item.val2} col2, #{item.val2} col3 FROM dual
</foreach>
</insert>
插入完成之后返回 sessionId
在 Mybatis 中,返回某个 property 可以用 SelectKey。SelectKey 是 Insert 的子标签,实现原理是在执行插入语句之前先做一次 SelectKey 的子查询,此处,可以将子查询的结果赋值到查询的参数当中,例如
public class Foo {
private int id;
private String col2;
private String col3;
}
public interface FooDao {
void sampleInsert(Foo foo);
}
<insert id="sampleInsert" useGeneratedKeys="true" parameterType="Foo" keyProperty="id">
<selectKey keyProperty="id" order="BEFORE" resultType="int">
SELECT SESSION_SEQUENCE.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO target_table (col1, col2, col3)
VALUES (#{id}, #{col2}, #{col3})
</insert>
当插入结束之后,参数 foo.id 就会是 SESSION_SEQUENCE 的自增值(注意在 Dao 中不能用 @Param() 标注参数)。
然而这种方式只支持单条记录的插入,Oracle 中批量插入的情况就无法完成赋值了。所以此处只能分成两步来做:
获取
sessionId(SESSION_SEQUENCE.NEXTVAL)
批量插入带 sessionId 的记录
或者
用 SelectKey 选出
sessionId(SESSION_SEQUENCE.NEXTVAL)
并批量插入记录获取
sessionId(SESSION_SEQUENCE.CURRVAL)
注意上述第二种方式,需要保证两个方法在同一个 Transaction 里面,否则 Sequence 的值会不一致。
注意事项
oracle中用insert into xxx values (xxx,xxx),(xxx,xxx) 这种语法应该通不过的,会报错,ORA-00933: SQL 命令未正确结束,要用这种
insert into t_test (id, prop_name)
select 1,'aaa' from dual union all
select 2,'bbb' from dual union all
select 3,'ccc' from dual;
Reference
The Ultimate Guide to Oracle INSERT ALL Statement
Oracle INSERT INTO SELECT
Inserting Multiple Rows Using a Single Statement
Inserting multiple rows with sequence in Oracle
来源:https://juejin.cn/post/7049975534029111327


猜你喜欢
- 本篇分析ArrayList的源码,在分析之前先跟大家谈一谈数组。数组可能是我们最早接触到的数据结构之一,它是在内存中划分出一块连续的地址空间
- 一、如何显示assets/license.txt(中文)的内容? (1)方法1:InputStream.available()得到字节数,然
- 本文实例讲述了Android AutoCompleteTextView连接数据库自动提示的方法。分享给大家供大家参考,具体如下:这个简单例子
- SessionFactory在Hibernate中实际上起到了一个缓冲区的作用 他缓冲了HI
- springboot static调用service为null@PostConstruct注解好多人以为是Spring提供的。其实是Java
- 中文乱码问题真的是一个很棘手的问题,特别是从前台传到后台之后,都不知道问题出在哪里了。现在分享解决javaWEB中前后台中文乱码问题的3种方
- Spring官网上有一篇Getting Start,介绍了如何使用Docker发布Spring Boot应用,算是比较详细了,不过有些细节没
- 一开始我就纳闷了,怎么调试都只是一个光溜溜的界面,右侧的工具栏都没有如图:就一个光秃秃的界面,什么都没有,这就对调试很不方便于是我就试了试各
- 目录1.@ 根据id更新2.@ 条件构造器作为参数进行更新3.@ lambda构造器mybatisplus update语句为null时没有
- 前面介绍了Spring Boot 整合mybatis 使用注解的方式实现数据库操作,介绍了如何自动生成注解版的mapper 和pojo类。
- Threadlocal有什么用:简单的说就是,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自
- 前言相信大家应该都有所体会,在以前我们要实现流动性布局,比较繁琐,Google开源了一个项目叫FlexboxLayout,相信大家都不陌生。
- 1、一次性添加多行注释的快捷键首先选中要注释区域,然后ctrl+/ 这个是多行代码分行注释,每行一个注释符号ctrl+shift+/ 这个是
- 一.前言在日常的开发中,经常需要对方法参数进行校验(非空、长度等)。如果采用hardcode去校验(if..else..),会造成代码冗余,
- 本文实例讲述了Android EditText自定义样式的方法。分享给大家供大家参考,具体如下:1.去掉边框EditText的backgro
- 一、JPA介绍JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系
- 本文实例讲述了C#图像伪彩色处理方法。分享给大家供大家参考。具体如下://灰度图转伪彩色图像函数public Bitmap PGrayToC
- 1.在java代码中(SplashActivity继承AppCompatActivity时无效)2.在manifest.xml中改Theme
- 新建Rest服务接口:[ServiceContract]public interface IService1{ &nb
- instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:boolean result = o