Mybatis-Plus大批量插入数据到MySQL

MyBatis-Plus的saveBatch方法

@GetMapping("/save1")
public void save1() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // mybatis-plus
    long start = System.currentTimeMillis();
    mallOrderService.saveBatch(orderList);
    System.out.println("mybatis-plus的【savaBatch】插入数据,耗时:" + (System.currentTimeMillis() - start) + "ms");
}

MyBatis-Plus的xml方式

@GetMapping("/save2")
public void save2() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // xml
    long start = System.currentTimeMillis();
    mallOrderService.saveBatchXml(orderList);
    System.out.println("mybatis-plus的【xml拼接sql】插入数据,耗时:" + (System.currentTimeMillis() - start) + "ms");
}
<insert id="saveBatch">
    insert into mall_order (order_id,
    customer_id,
    order_status,
    payment_method,
    total_amount,
    shipping_fee,
    coupon_discount,
    order_date,
    payment_time,
    shipping_address,
    receiver_name,
    receiver_phone)
    values
    <foreach collection="orderList" item="item" separator=",">
        (#{item.orderId},
        #{item.customerId},
        #{item.orderStatus},
        #{item.paymentMethod},
        #{item.totalAmount},
        #{item.shippingFee},
        #{item.couponDiscount},
        #{item.orderDate},
        #{item.paymentTime},
        #{item.shippingAddress},
        #{item.receiverName},
        #{item.receiverPhone})
    </foreach>
</insert>

MyBatis-Plus的批量插入器

@GetMapping("/save3")
public void save3() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // insertBatchSomeColumn
    long start = System.currentTimeMillis();
    mallOrderService.insertBatchSomeColumn(orderList);
    System.out.println("mybatis-plus的【insertBatchSomeColumn】插入数据,耗时:" + (System.currentTimeMillis() - start) + "ms");
}
package com.qiangesoft.batchsave.config;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;

import java.util.List;

/**
 * sql注入器
 *
 * @author qiangesoft
 * @date 2024-04-11
 */
public class InsertBatchSqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        // super.getMethodList() 保留 Mybatis Plus 自带的方法
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        // 添加自定义方法:批量插入,方法名为 insertBatchSomeColumn
        // bean mapper中的方法名也是insertBatchSomeColumn 须和内部定义好的方法名保持一致。
        methodList.add(new InsertBatchSomeColumn());
        return methodList;
    }

}
package com.qiangesoft.batchsave.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * mybatis-plus配置
 *
 * @author qiangesoft
 * @date 2024-04-11
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public InsertBatchSqlInjector insertBatchSqlInjector() {
        return new InsertBatchSqlInjector();
    }

}

package com.qiangesoft.batchsave.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qiangesoft.batchsave.entity.MallOrder;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * <p>
 * 订单信息表 Mapper 接口
 * </p>
 *
 * @author qiangesoft
 * @since 2024-05-07
 */
public interface MallOrderMapper extends BaseMapper<MallOrder> {

    /**
     * 批量插入 仅适用于mysql
     *
     * @param orderList
     */
    Integer insertBatchSomeColumn(List<MallOrder> orderList);
}

SqlSession手动提交

@GetMapping("/save4")
public void save4() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // 手动提交
    long start = System.currentTimeMillis();
    mallOrderService.manualCommit(orderList);
    System.out.println("sqlSession的【手动提交】插入数据100000条,耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Autowired
private SqlSessionFactory sqlSessionFactory;

@Override
public void manualCommit(List<MallOrder> orderList) {
    // 关闭自动提交
    SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    MallOrderMapper mallOrderMapper = sqlSession.getMapper(MallOrderMapper.class);
    for (MallOrder mallOrder : orderList) {
        mallOrderMapper.insert(mallOrder);
    }

    sqlSession.commit();
    sqlSession.clearCache();
    sqlSession.close();
}

循环执行MyBatis-Plus的save方法

@GetMapping("/save5")
public void save5() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // 循环插入
    long start = System.currentTimeMillis();
    for (MallOrder mallOrder : orderList) {
        mallOrderService.save(mallOrder);
    }
    System.out.println("mybatis-plus的【循环】插入数据100000条,耗时:" + (System.currentTimeMillis() - start) + "ms");
}

Statement批量执行

@GetMapping("/save6")
public void save6() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // statement.executeBatch
    long start = System.currentTimeMillis();
    mallOrderService.executeBatch(orderList);
    System.out.println("statement的【executeBatch】插入数据100000条,耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Override
public void executeBatch(List<MallOrder> orderList) {
    String sql = "insert into mall_order (" +
            "customer_id," +
            "order_status," +
            "payment_method," +
            "total_amount," +
            "shipping_fee," +
            "coupon_discount," +
            "order_date," +
            "payment_time," +
            "shipping_address," +
            "receiver_name," +
            "receiver_phone) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";

    SqlSession sqlSession = null;
    Connection connection = null;
    PreparedStatement statement = null;
    try {
        sqlSession = sqlSessionFactory.openSession();
        connection = sqlSession.getConnection();
        connection.setAutoCommit(false);
        statement = connection.prepareStatement(sql);

        for (MallOrder mallOrder : orderList) {
            // 主键自增不设置
//                statement.setLong(0, mallOrder.getOrderId());
            statement.setLong(1, mallOrder.getCustomerId());
            statement.setInt(2, mallOrder.getOrderStatus());
            statement.setInt(3, mallOrder.getPaymentMethod());
            statement.setBigDecimal(4, mallOrder.getTotalAmount());
            statement.setBigDecimal(5, mallOrder.getShippingFee());
            statement.setBigDecimal(6, mallOrder.getCouponDiscount());
            statement.setObject(7, mallOrder.getOrderDate());
            statement.setObject(8, mallOrder.getPaymentTime());
            statement.setString(9, mallOrder.getShippingAddress());
            statement.setString(10, mallOrder.getReceiverName());
            statement.setString(11, mallOrder.getReceiverPhone());
            statement.addBatch();
        }
        statement.executeBatch();
        connection.commit();
    } catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

MyBatis-Plus的saveBatch方法+异步任务

@GetMapping("/save7")
public void save7() {
    // 数据准备
    List<MallOrder> orderList = getMallOrderList();

    // 异步任务处理
    long start = System.currentTimeMillis();
    mallOrderService.saveBatchAsync(orderList);
    System.out.println("mybatis-plus的【异步任务处理】插入数据100000条,耗时:" + (System.currentTimeMillis() - start) + "ms");
}
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private PlatformTransactionManager transactionManager;

@Override
public void saveBatchAsync(List<MallOrder> orderList) {
    int count = orderList.size();
    // 每批次插入的数据量
    int pageSize = 1000;
    // 线程数
    int threadNum = count % pageSize == 0 ? (count / pageSize) : (count / pageSize + 1);
    CountDownLatch countDownLatch = new CountDownLatch(threadNum);

    for (int i = 0; i < threadNum; i++) {
        int startIndex = i * pageSize;
        int endIndex = Math.min(count, (i + 1) * pageSize);
        List<MallOrder> subList = orderList.subList(startIndex, endIndex);
        threadPoolTaskExecutor.execute(() -> {
            DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
            TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
            try {
                // 保存数据
                this.saveBatch(subList);
                transactionManager.commit(status);
            } catch (Exception exception) {
                transactionManager.rollback(status);
                throw exception;
            } finally {
                countDownLatch.countDown();
            }
        });
    }
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

测试

CREATE TABLE `mall_order`
(
    `order_id`         BIGINT         NOT NULL AUTO_INCREMENT COMMENT '订单ID(主键)',
    `customer_id`      BIGINT         NOT NULL COMMENT '客户ID(关联customer表)',
    `order_status`     tinyint(4)     NOT NULL DEFAULT 1 COMMENT '订单状态 1-待支付 2-已支付 3-待发货 4-已发货 5-已完成 6-已取消',
    `payment_method`   tinyint(4)     NULL     DEFAULT null COMMENT '支付方式; 1-现金 2-支付宝 3-微信 4-银行卡',
    `total_amount`     DECIMAL(10, 2) NOT NULL COMMENT '订单总金额',
    `shipping_fee`     DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '运费',
    `coupon_discount`  DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '优惠券减免金额',
    `order_date`       DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '下单日期',
    `payment_time`     DATETIME                DEFAULT NULL COMMENT '支付时间',
    `shipping_address` VARCHAR(255)   NULL COMMENT '收货地址',
    `receiver_name`    VARCHAR(50)    NULL COMMENT '收货人姓名',
    `receiver_phone`   VARCHAR(20)    NULL COMMENT '收货人电话',
    PRIMARY KEY (`order_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='订单信息表';
package com.qiangesoft.batchsave.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * <p>
 * 订单信息表
 * </p>
 *
 * @author qiangesoft
 * @since 2024-05-07
 */
@Getter
@Setter
@Accessors(chain = true)
@TableName("mall_order")
public class MallOrder implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 订单ID(主键)
     */
    @TableId(value = "order_id", type = IdType.INPUT)
    private Long orderId;

    /**
     * 客户ID(关联customer表)
     */
    private Long customerId;

    /**
     * 订单状态 1-待支付 2-已支付 3-待发货 4-已发货 5-已完成 6-已取消
     */
    private Integer orderStatus;

    /**
     * 支付方式; 1-现金 2-支付宝 3-微信 4-银行卡
     */
    private Integer paymentMethod;

    /**
     * 订单总金额
     */
    private BigDecimal totalAmount;

    /**
     * 运费
     */
    private BigDecimal shippingFee;

    /**
     * 优惠券减免金额
     */
    private BigDecimal couponDiscount;

    /**
     * 下单日期
     */
    private LocalDateTime orderDate;

    /**
     * 支付时间
     */
    private LocalDateTime paymentTime;

    /**
     * 收货地址
     */
    private String shippingAddress;

    /**
     * 收货人姓名
     */
    private String receiverName;

    /**
     * 收货人电话
     */
    private String receiverPhone;


}

1000条

/**
 * 构建数据
 *
 * @return
 */
private static List<MallOrder> getMallOrderList() {
    List<MallOrder> orderList = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
        MallOrder mallOrder = new MallOrder();
        mallOrder.setCustomerId(1L);
        mallOrder.setOrderStatus(1);
        mallOrder.setPaymentMethod(1);
        mallOrder.setTotalAmount(BigDecimal.valueOf(12));
        mallOrder.setShippingFee(BigDecimal.valueOf(1));
        mallOrder.setCouponDiscount(BigDecimal.valueOf(0));
        mallOrder.setOrderDate(LocalDateTime.now());
        mallOrder.setPaymentTime(LocalDateTime.now());
        mallOrder.setShippingAddress("哈哈");
        mallOrder.setReceiverName("暂时");
        mallOrder.setReceiverPhone("13211111111");
        orderList.add(mallOrder);
    }
    return orderList;
}

在这里插入图片描述

10000条

/**
 * 构建数据
 *
 * @return
 */
private static List<MallOrder> getMallOrderList() {
    List<MallOrder> orderList = new ArrayList<>();
    for (int i = 0; i < 1000; i++) {
        MallOrder mallOrder = new MallOrder();
        mallOrder.setCustomerId(1L);
        mallOrder.setOrderStatus(1);
        mallOrder.setPaymentMethod(1);
        mallOrder.setTotalAmount(BigDecimal.valueOf(12));
        mallOrder.setShippingFee(BigDecimal.valueOf(1));
        mallOrder.setCouponDiscount(BigDecimal.valueOf(0));
        mallOrder.setOrderDate(LocalDateTime.now());
        mallOrder.setPaymentTime(LocalDateTime.now());
        mallOrder.setShippingAddress("哈哈");
        mallOrder.setReceiverName("暂时");
        mallOrder.setReceiverPhone("13211111111");
        orderList.add(mallOrder);
    }
    return orderList;
}

在这里插入图片描述

总结

由此可见:
MyBatis-Plus的批量插入器、Statement批量执行、MyBatis-Plus的saveBatch方法+异步任务这三种方式较快。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/600093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】HTTPS

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;Linux 目录 &#x1f449;&#x1f3fb;HTTPS协议概念&#x1f449;&#x1f3fb;加密为什么要进行加密 &#x1f449;&#x1f3fb;常见的加密方式对称加密…

Backblaze发布2024 Q1硬盘故障质量报告-1

作为一家在2021年在美国纳斯达克上市的云端备份公司&#xff0c;Backblaze一直保持着对外定期发布HDD和SSD的故障率稳定性质量报告&#xff0c;给大家提供了一份真实应用场景下的稳定性分析参考数据。 截至2024年第一季度末&#xff0c;Backblaze在其全球数据中心的云存储服务器…

15.计算机网络

1.物理层的互联设备 中继器 和 集线器 2.集线器可以看做特殊的多路中继器 集线器 不可以做到自动寻址的功能 3.数据链路层 网桥 和 交换机 4.交换机是多端口网桥 5.网络层 路由器 6.应用层 网关 7.广播域 网络层 可以形成多个广播域 冲突域 网络层数据链路层 可以形成多个冲突域…

Codeforces Round 942 (Div. 2) A-D1

题目&#xff1a; Codeforces Round 942 (Div. 2) D2有缘再补吧… A. Contest Proposal 题意 两个升序&#xff08;不降&#xff09;的序列a和b&#xff0c;可以在a的任意位置插入任意数&#xff08;要保持升序&#xff09;&#xff0c;使对任意i&#xff0c;有a[i] < b[…

shell脚本编写-测试同一网段内主机是否在线

除了可以使用ansible自动化运维工具判断主机是否在线以外&#xff0c;还可以通过编写Shell脚本来实现。 1、编写脚本 #! /bin/bash #测试192.168.81.0/24网段中哪些主机处于开机状态&#xff0c;哪些主机处于关机状态# #方法一&#xff1a;使用for循环判断 # for i in {1..25…

红海云OA存在任意文件上传漏洞【附poc】

漏洞复现 1、fofa poc见文末 body"RedseaPlatform" 打开burp进行抓包发送到repeater&#xff0c;如下图所示&#xff1a; 打入poc&#xff08;文末获取&#xff09;&#xff0c;成功上传。 「你即将失去如下所有学习变强机会」 学习效率低&#xff0c;学不到实战内…

聊聊 ASP.NET Core 中间件(三):如何创建自己的中间件?

前言 本质上&#xff0c;中间件类也是一个普通的 .NET 类&#xff0c;它不需要继承任何父类或者实现任何接口。 但是有几个约定&#xff1a; 需要有一个构造方法构造方法至少要有一个 RequestDelegate 类型的参数&#xff0c;用来指向下一个中间件。需要定义一个名字为 Invo…

后仿中必须读懂的User-defined primitives(UDP)

一 UDP定义规则 UDP&#xff0c;全名&#xff1a;User-defined primitives。 用户自己定义的原语。 UDP可分为&#xff1a;combinational UDP&#xff08;组合逻辑&#xff09;和 sequential UDP&#xff08;时序逻辑&#xff09;。 1.1 组合逻辑UDP combinational UDP用于…

软件系统测试方案书(测试计划-Word原件)

2 引言 2.1 编写目的 2.3 测试人员 2.4 项目背景 2.5 测试目标 2.6 简写和缩略词 2.7 参考资料 2.8 测试提交文档 2.9 测试进度 3 测试环境 3.1 软硬件环境 4 测试工具 5 测试策略 5.1 测试阶段划分及内容 5.1.1 集成测试 5.1.2 系统测试 5.1.2.1 功能测试 5.…

Autosar NvM配置-手动配置Nvblock及使用-基于ETAS软件

文章目录 前言NvDataInterfaceNvBlockNvM配置SWC配置RTE Mapping使用生成的接口操作NVM总结前言 NVM作为存储协议栈中最顶层的模块,是必须要掌握的。目前项目基本使用MCU带的Dflash模块,使用Fee模拟eeprom。在项目前期阶段,应该充分讨论需要存储的内容,包括应用数据,诊断…

在Ubuntu上安装docker

一、安装docker 更新系统包列表&#xff1a; sudo apt-get update安装必要的依赖软件包&#xff0c;使apt可以通过HTTPS使用repository。 sudo apt-get install apt-transport-https ca-certificates curl software-properties-common添加Docker的阿里云GPG密钥&#xff1a;…

算法提高之树的最长路径

算法提高之树的最长路径 核心思想&#xff1a;树形dp 枚举路径的中间节点用f1[i] 表示i的子树到i的最长距离,f2[i]表示次长距离最终答案就是max(f1[i]f2[i]) #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N …

数据结构(c):队列

目录 &#x1f37a;0.前言 1.什么是队列 2. 队列的实现 2.1定义队列节点 2.2定义队列 2.3队尾入队列 2.4判断队列是否为空 2.5队头出队列 2.6 队列首元素 2.7队尾元素 2.8队列内的元素个数 2.9销毁队列 3.试运行 &#x1f48e;4.结束语 &#x1f37a;0.前言 言C之…

[笔记] Win11 Microsoft Store App 离线下载

微软应用商店无法下载或下载缓慢解决方法 在一些环境下 Microsoft Store 下载速度缓慢&#xff0c;或者需要账号登录才能安装的场景&#xff0c;可以通过找到对应的离线安装包的形式进行安装。 Micorsoft Store 中的离线安装包一般后缀为 AppxBundle 和 Appx。以 Ubuntu 为例…

(四)JSP教程——request内置对象

request对象是将客户端浏览器数据提交给服务器端JSP页面的唯一数据通道&#xff0c;通过该通道JSP页面能够获取浏览器信息、form表单信息、URL参数信息等。 1.from表单向JSP文件传递数据 form表单是浏览器向服务器传递数据的一种基本机制&#xff0c;包含两种方式&#xff1a;…

智慧校园功平台能结构

高等教育信息化是促进高等教育改革创新和提高质量的有效途径&#xff0c;是教育信息化发展的创新前沿。进一步加强基础设施和信息资源建设&#xff0c;重点推进信息技术与高等教育的深度融合&#xff0c;能促进教育内容、教学手段和方法现代化&#xff0c;创新人才培养、科研组…

卷价格不如卷工艺降本增效狠抓模块规范化设计

俗话说&#xff0c;“卷价格不如卷工艺”&#xff0c;这意味着在追求成本控制和效率提升的过程中&#xff0c;蓝鹏的领导认为蓝鹏应该更注重工艺的优化和创新&#xff0c;而不仅仅是价格的竞争。而模块规范化设计正是实现这一目标的有效途径。 模块规范化设计可以提高生产效率…

推荐网站(5)Pika文字生成视频,ai视频创作

今天推荐一个网站&#xff0c;Pika文字生成视频&#xff0c;通过问题描述&#xff0c;帮我们生成对应的视频&#xff0c;非常的实用。 比如输入&#xff1a;一只小狗在河边洗澡 当然我们还可以在生成的视频上编辑 点击编辑后出来一些属性&#xff0c;可以修改区域&#xff0c…

TitanIDE安装常见问题解答

在软件开发和编程的世界里&#xff0c;集成开发环境&#xff08;IDE&#xff09;扮演着至关重要的角色。TitanIDE作为一款功能强大的开发工具&#xff0c;深受广大开发者的喜爱。然而&#xff0c;在安装和使用TitanIDE的过程中&#xff0c;开发者们往往会遇到一些问题和挑战。针…

cmake进阶:目录属性之 INCLUDE_DIRECTORIES说明一

一. 简介 前一篇文章学习了 cmake的一些目录属性&#xff0c;其中最重要的是 头文件搜索路径。文章如下&#xff1a; cmake进阶&#xff1a;目录属性说明一-CSDN博客 本文主要学习 一个目录属性 INCLUDE_DIRECTORIES&#xff0c;即头文件搜索路径。 二. cmake进阶&#xff1…