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
   ...
);

MyBatisdynamic 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

标签:mybatis,批量插入,oracle
0
投稿

猜你喜欢

  • Android 沉浸式状态栏及悬浮效果

    2023-07-29 23:03:15
  • Android引导页面的简单实现

    2023-08-06 04:24:32
  • SpringBoot下如何实现支付宝接口的使用

    2023-11-06 14:26:15
  • MyBatis图文并茂讲解注解开发一对多查询

    2023-02-18 08:18:40
  • 一篇文章教你使用枚举来实现java单例模式

    2023-08-23 22:24:36
  • Android自定义view实现滚动选择控件详解

    2022-12-05 09:54:06
  • Android中AlertDialog 点击按钮后不关闭对话框的功能

    2023-08-04 22:55:50
  • 利用C#快速查出哪些QQ好友空间屏蔽了自己

    2023-10-07 22:14:28
  • Java ClassLoader虚拟类实现代码热替换的示例代码

    2022-06-25 03:08:52
  • Java非法字符: ‘\\ufeff‘问题及说明

    2023-02-01 09:06:07
  • Spring Security自定义登录原理及实现详解

    2022-11-20 21:57:39
  • SpringCloud2020整合Nacos-Bootstrap配置不生效的解决

    2023-11-29 02:42:47
  • java简单解析xls文件的方法示例【读取和写入】

    2022-04-15 19:30:43
  • Java 1.8使用数组实现循环队列

    2022-02-11 04:00:10
  • SpringBoot自动配置源码深入刨析讲解

    2022-04-03 14:38:06
  • C#多态的三种实现方式(小结)

    2022-12-29 18:10:57
  • autojs模仿QQ长按弹窗菜单实现示例详解二

    2022-10-20 07:04:37
  • 一文带你搞懂Java中的泛型和通配符

    2023-12-10 16:15:07
  • C#中WinForm控件的拖动和缩放的实现代码

    2023-09-27 23:54:08
  • android获取当前手机号示例程序

    2022-09-10 04:57:06
  • asp之家 软件编程 m.aspxhome.com