oracle 批量插入实战(手写一个批量插入小工具)

前几天遇到一个需要实现oracle批量插入的需求,于是我自己疯狂的百度,搞了一个oracle批量插入的小工具。

批量插入语句格式
insert into tableName(column1(主键),column2,column3...)
  select value1 column1,value2 column2,value3 column3 from dual
  union all
  select value1 column1,value2 column2,value3 column3 from dual
  union all
  select value1 column1,value2 column2,value3 column3 from dual
  union all
  select value1 column1,value2 column2,value3 column3 from dual

例子:

insert into Student(id, name, sex, age, tel)
select '24' id, 'jack' name, '男' sex, 22 age, '13345674567' tel from dual
union select '25' id, 'jack' name, '男' sex, 22 age, '13345674567' tel from dual
union select '26' id, 'jack' name, '男' sex, 32 age, '13345674567' tel from dual
批量生成主键ID

首先定义一oracle的个主键序列:”HZERO”.”CFS_CST_SC_RECORD_S”.nextval,具体的自己根据自己的业务上来定义

定义主键序列sql:

minvalue:序列最小值

maxvalue:序列最大值

start with:序列起始值

increment by:序列每查一次增加的步长

cache:预缓存。当访问量很大的时候可以先缓存20个序列提供使用,每一次扩容都是按照20个进行增加

-- Create sequence
create sequence CFS_CST_SC_RECORD_S
minvalue 1
maxvalue 9999999999999999999999999999
start with 1
increment by 1
cache 20;

Java代码获取主键ID

第一种方式:使用Java代码获取主键ID后在赋值给插入的数据。

简单获取主键的方式如下:

select "HZERO"."CFS_CST_SC_RECORD_S".nextval from dual;

但是如何批量呢?我尝试过这个!

oracle 批量插入实战(手写一个批量插入小工具)

无法批量获取难道我只能单条数据循环获取,那要是数据上万咋办,岂不是直接炸了。于是有了下面的方案:

1、找一张或者自己定义一张大数据量的表,比如:CFS_CST_SC_SECORD

2、实现sql

oracle 批量插入实战(手写一个批量插入小工具)

上述方案存在一些缺陷:

1、虽然使用了CFS_CST_SC_SECORD表但是没有使用该表的任何数据,只是利用了表里面存在的数据量来查询主键ID,代码可读性比较差。

2、如果表里面的数据突然清空或者少了,查询的主键ID查询不出来或者数量变少。

3、如果查询的主键ID总条数大于CFS_CST_SC_SECORD表中数据也会出现主键ID数量变少的情况,还需要再java代码里面查询CFS_CST_SC_SECORD表总数量并且根据这个数量做分批次查询。

配置主键ID的自增序列

第二种方式:在表设计中配置表主键ID的序列默认自增,这样在我们插入的时候不需要插入ID字段

oracle 批量插入实战(手写一个批量插入小工具)

插入格式:

insert into tableName(column1, column2, column3...)
  select value1 column1, value2 column2, value3 column3 from dual
  union all
  select value1 column1, value2 column2, value3 column3 from dual
  union all
  select value1 column1, value2 column2, value3 column3 from dual
  union all
  select value1 column1, value2 column2, value3 column3 from dual

例子:

insert into Student(name, sex, age, tel)
select 'jack' name, '男' sex, 22 age, '13345674567' tel from dual
union select 'jack' name, '男' sex, 22 age, '13345674567' tel from dual
union select 'jack' name, '男' sex, 32 age, '13345674567' tel from dual

注意:配置了ID的自增序列,在拼接插入语句的时候千万不要拼接主键ID(上图就是SC_ID字段)字段

批量插入工具

定义一个对象字段的批量插入注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BatchInsertField {
    String fieldName() default "";
    // 插入时忽略字段
    boolean insertIgnore() default false;
    // 日期格式
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}

注解的使用:

注意以下几点

  1. 对象必须带有 @Table注解
  2. 主键必须带有 @Id注解
  3. 与批量更新(先删除在插入带有主键ID值得数据)做对比,可配置批量插入忽略字段 insertIgnore = true,表示在插入的时候忽略这个字段,在更新时候保留这个字段。
@Data
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@Table(name = "CFS_CST_SC_RECORD")
public class ScRecord extends AuditDomain {
    @ApiModelProperty("主键")
    @Id
    @GeneratedValue
    @BatchInsertField(fieldName = "SC_ID", insertIgnore = true)
    private Long scId;

    @ApiModelProperty(value = "结转编号", required = true)
    @NotBlank
    @BatchInsertField(fieldName = "SC_NUMBER")
    private String scNumber;

    @ApiModelProperty(value = "房源内码", required = true)
    @NotBlank
    @BatchInsertField(fieldName = "ROOM_CODE")
    private String roomCode;

    @ApiModelProperty(value = "营销系统交房日期,格式YYYY-MM-DD")
    @BatchInsertField(fieldName = "SD_DELIVERY_DATE", dateFormat = "yyyy-MM-dd")
    private Date sdDeliveryDate;
}

批量插入工具类:

BatchInsertUtil.java

package org.xd.cfs.common.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import io.choerodon.core.exception.CommonException;
import org.hzero.core.base.BaseConstants;
import org.xd.cfs.common.annotations.BatchInsertField;

import javax.persistence.Id;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 批量插入sql工具,需要在集合对象中配置@BatchInsertField注解和@Table,如果插入需要忽略设置insertIgnore = true
 * 例: @Table(name = "table_name")
 * 例: @BatchInsertField(fieldName = "SC_ID", insertIgnore = true)  插入时忽略字段
 * @author can.he@hand-china.com
 * @description
 * @date 2022/3/18 15:14
 */
public class BatchInsertUtil {
    private static final String insertDateFormat = "yyyy-MM-dd hh24:mi:ss";

    /**
     * @description 获取批量插入sql
     * @param collection 批量插入的集合
     * @return java.lang.String 返回的sql语句
     * @author can.he@hand-china.com
     * @date 2022/3/18 16:50
     */
    public static String getInsertSql(Collection collection) {
        List list = new ArrayList<>(collection);
        StringBuilder sql = new StringBuilder();
        Class cs = list.get(0).getClass();
        String tableName = cs.getAnnotation(Table.class).name();
        String fieldStr = getFieldNameStr(list.get(0));
        String header = "INSERT INTO "+tableName+" ( " + fieldStr + " )";
        sql.append(header);
        for (int i = 0; i < list.size(); i++) {
            Object item  =  list.get(i);
            String valuesStr = getParamsStr(item);
            if (i == 0) {
                sql.append(" SELECT ").append(valuesStr).append(" FROM DUAL");
            }else {
                sql.append(" UNION ALL SELECT ").append(valuesStr).append(" FROM DUAL");
            }
        }
        return sql.toString();
    }

    // 插入列拼接
    public static String getFieldNameStr(Object object) {
        StringBuilder str = new StringBuilder();
        boolean isAdd = false;
        Field[] fields = object.getClass().getDeclaredFields();
        List fieldList = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(Id.class)).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(fieldList)) {
            throw new CommonException("实体对象@Id注解不能为空");
        }
        try {
            Field field = fieldList.get(0);
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            isAdd = ObjectUtil.isEmpty(field.get(object));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        for (Field field : fields) {
            if (field.isAnnotationPresent(BatchInsertField.class)) {
                boolean insertIgnore = field.getAnnotation(BatchInsertField.class).insertIgnore();
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                // 插入忽略
                if (isAdd && insertIgnore) {
                    continue;
                }
                String fieldName = field.getAnnotation(BatchInsertField.class).fieldName();
                str.append(str.length() == 0 ? fieldName : ", " + fieldName);
            }
        }
        return str.toString();
    }

    // 插入值拼接
    public static String getParamsStr(Object object) {
        StringBuilder str = new StringBuilder();
        boolean isAdd = false;
        Field[] fields = object.getClass().getDeclaredFields();
        List fieldList = Arrays.stream(fields).filter(field -> field.isAnnotationPresent(Id.class)).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(fieldList)) {
            throw new CommonException("实体对象@Id注解不能为空");
        }
        try {
            Field field = fieldList.get(0);
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            isAdd = ObjectUtil.isEmpty(field.get(object));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        for (Field field : fields) {
            if (field.isAnnotationPresent(BatchInsertField.class)) {
                String fieldName = field.getAnnotation(BatchInsertField.class).fieldName();
                boolean insertIgnore = field.getAnnotation(BatchInsertField.class).insertIgnore();
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                Object value = null;
                try {
                    value = field.get(object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                // 判断是否插入忽略字段
                if (isAdd && insertIgnore) {
                    continue;
                }
                // 对字符串进行处理
                if ("class java.lang.String".equals(field.getGenericType().toString())) {
                    // 对空字符串和字符串进行处理
                    value = ObjectUtil.isEmpty(value) ? null : "'" + value + "'";
                }
                // 对日期进行处理
                if ("class java.util.Date".equals(field.getGenericType().toString())) {
                    // 对空字符串和字符串进行处理
                    if (ObjectUtil.isNotEmpty(value)) {
                        String dateFormat = field.getAnnotation(BatchInsertField.class).dateFormat();
                        String date = DateUtil.format((Date) value, dateFormat);
                        dateFormat = ObjectUtil.equal(BaseConstants.Pattern.DATETIME, dateFormat) ? insertDateFormat : dateFormat;
                        value = "to_date('" + date + "', '"+dateFormat+"')";
                    }else {
                        value = null;
                    }
                }
                value = str.length() == 0 ? value : ", " + value;
                str.append(value).append(" ").append(fieldName);
            }
        }
        return str.toString();
    }
}
批量插入工具使用
// 批量插入结转记录
List> batchList = ListUtils.partition(updateScRecordList, 1000);
batchList.forEach(itemList -> {
    String sql = BatchInsertUtil.getInsertSql(itemList);
    scRecordRepository.handleBatchInsert(sql);
});

handleBatchInsert:是执行sql的一个service接口


    ${sql}

通过以上操作实现了oracle的批量插入。其实还有一个方案,就是开发一个oracle的存储过程,在存储过程里面实现循环插入,这样也可以减少java对数据库的访问来提高插入效率。

Original: https://www.cnblogs.com/cqhcan/p/16054486.html
Author: HCAN
Title: oracle 批量插入实战(手写一个批量插入小工具)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/714017/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 设计恰如其分的架构

    远在2009年,Martin Fowler与Rebecca Parsons在QCon SF做了一次题为Agilists and Architects: Allies not Adv…

    技术杂谈 2023年5月31日
    0100
  • 【5】2022年7月

    7月3日 总结3号的一天就是只有一句话,”自己经历了什么只有自己最清楚,不要辜负自己经历的”。 7月3号凌晨2点,收拾好行李和整理房间,在网上购买日常用品到…

    技术杂谈 2023年7月10日
    085
  • 基于openlayers2军事标绘,开源[转]

    1.其实各GIS公司、网络上 都会有提供 军事标绘的功能,如arcgis 的,超图的、mapgis的;但大多数是非开源、且收费的。2.在这里要感谢超图,超图开源了标绘扩展符号库,我…

    技术杂谈 2023年5月31日
    085
  • 十分钟学会Golang开发gRPC服务

    gRPC是Google发起的一个开源RPC框架,使用HTTP/2传输协议,使用Protocol Buffers编码协议,相比RESTful框架的程序性能提高不少,而且当前流行的编程…

    技术杂谈 2023年7月11日
    065
  • Kubernetes 升级至 1.24 并将 container runtime 切换至 containerd

    Kubernetes 1.24 于 2022 年 5 月 3 日正式发布(详见 Kubernetes 1.24: Stargazer),1.24 版本的一个重大变化是从 kubel…

    技术杂谈 2023年5月30日
    072
  • [转帖]Apache Ignite(六):Ignite 的集群部署

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    技术杂谈 2023年5月30日
    080
  • 68.可能否

    sfsd posted @2022-09-28 08:39 随遇而安== 阅读(6 ) 评论() 编辑 Original: https://www.cnblogs.com/55zj…

    技术杂谈 2023年6月21日
    091
  • 前端经常使用插件使用文档 以及demo

    一.————————————&#82…

    技术杂谈 2023年5月31日
    094
  • 小i机器人

    php;gutter:true; //机器人回复 function xiaoirobot($openid, $content) { //定义app $app_key = &quot…

    技术杂谈 2023年5月31日
    078
  • 当类的泛型相关时,如何在两个泛型类之间创建类似子类型的关系

    哈喽大家好,我是阿Q! 事情是这个样子的…… 对话中的截图如下: 看了阿Q的解释,你是否也和”马小跳”一样存在疑问呢?请往👇看 我们…

    技术杂谈 2023年7月11日
    068
  • 星空

    大概是因为近视或者污染严重的缘故,我已经很少能看到星空了。更多时候抬起头,望见的只是无法穿透的黑夜。但星空就在那里,它不会因为我看不见它而消失。准确地说,星空一直在我们每个人的心里…

    技术杂谈 2023年6月21日
    085
  • html大文件传输功能

    javaweb上传文件 上传文件的jsp中的部分 上传文件同样可以使用form表单向后端发请求,也可以使用 ajax向后端发请求 通过form表单向后端发送请求 改进后的代码不需要…

    技术杂谈 2023年5月30日
    099
  • mysql 内部函数

    1. group_concat 返回一个字符串结果,该结果由分组中的值连接组合而成。 函数语法: group_concat( [DISTINCT] 要连接的字段 [Order BY…

    技术杂谈 2023年7月25日
    064
  • SQL52 获取employees中的first_name

    本题链接表结构如下所示(内容不完整):额外的要求是按照first_name最后两个字母升序进行输出。这里需要用到MySQL的字符串处理函数RIGHT。RIGHT函数的语法如下所示:…

    技术杂谈 2023年7月11日
    065
  • 初识CityEngine【转】

    一、CityEngine历史 二、CityEngine建模思想 1、生成城市地块 2、楼层房间切割 3、建模思想、流程 (1)、建筑生成思想 (2)、官方示意流程图 三、CityE…

    技术杂谈 2023年5月31日
    089
  • jsp注释<%– — %> 和 <!– –> 的区别?

    jsp页面注释代码推荐用 在jsp中,采用注释代码,浏览器还是会解析注释中的代码,而且用户使用浏览器查看页面源码的时候,注释的代码对于他们是可见的,所以不管是为了提高效率,还是为了…

    技术杂谈 2023年5月31日
    0104
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球