springboot+mybatis如何快速插入大量数据

news2024/12/25 13:20:07

在公司业务开发过程中,我们经常会遇到往数据库表中插入大量数据的场景,比如excel批量导入数据。那么该如何快速地插入数据呢?我们可以考虑使用批量插入来实现,该方案实测每秒能达到35000条,后附具体实现代码。接下来我就给大家说一下实现方式。

一. JDBC实现方案

用一个 for 循环,把数据一条一条地插入;生成一条插入 sql,类似这种 insert into user(name,pwd) values('aa','123'),('cc','123')...

第一种方案,是用 for语句循环插入:

  • 该方案的优势在于,JDBC 中的 PreparedStatement 有预编译功能,预编译之后会缓存起来。之后SQL执行会比较快,且 JDBC可以开启批处理,这个批处理执行非常给力。

  • 劣势在于,很多时候我们的 SQL 服务器和应用服务器可能并不是同一台,所以必须要考虑网络 IO。如果网络 IO 比较费时间的话,那么可能会拖慢 SQL 执行的速度。

第二种方案,是生成一条 SQL进行插入:

  • 该方案的优势在于,只有一次网络 IO。即使分片处理也只是数次网络 IO,所以这种方案不会在网络 IO 上花费太多时间。

  • 当然这种方案也有劣势。一是 SQL 太长了,甚至可能需要分片后批量处理;二是无法充分发挥 PreparedStatement 预编译的优势,SQL 要重新解析且无法复用;三是最终生成的 SQL 太长了,数据库管理器解析这么长的 SQL 也需要时间。

我们接下来会采用第二种方案进行实现。

二. 具体实现思路

  1. 如果我们想要拉高插入效率,肯定不能够一条一条地插入了,必须得使用foreach批量插入;

  2. 采用多线程进行异步插入,提升性能;

  3. 我们不可能单次提交多个insert,大量的插入操作会很耗时,短时间内完不成,可以采用定时任务来实现。

接下来我们就来说说具体该怎么利用代码进行实现。

三. 代码实现

本案例主要是基于SpringBoot整合mybatis进行实现。

1.导入依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

2.创建启动类

@SpringBootApplication  //引导类核心注解
@EnableScheduling //开启定时任务
public class BatchApplication {
    public static void main(String[] args) {
        SpringApplication.run(BatchApplication.class,args);
    }
}

3.配置文件application.yml

server:
  port: 9999  # 指定端口号
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123
mybatis:
  mapper-locations: classpath:mybatis/*.xml   #指定mapper映射文件路径
  type-aliases-package: com.qfedu.model  # 别名

4.创建表与实体类User

创建表:

CREATE TABLE `user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(30) DEFAULT NULL,
  `pwd` VARCHAR(20) DEFAULT NULL,
  `sex` INT(11) DEFAULT NULL,
  `birthday` DATETIME DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8

注意:MyISAM效率会比INNODB快。

User.java

@Data
public class User {
    private int id;
    private String username;
    private String pwd;
    private int sex;
    private LocalDate birthday;
}

5.持久层mapper和映射文件

UserMapper.java

@Mapper
public interface UserMapper {
    void insertBatch(@Param("userList") List<User> userList);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.mapper.UserMapper">
    <insert id="addList" parameterType="User"  >
        insert  into user (username,pwd,sex,birthday) values
        <foreach collection="list" item="item" separator=",">
            (#{item.username}, #{item.pwd}, #{item.sex}, #{item.birthday})
        </foreach>
    </insert>
</mapper>

6.开启定时任务

SpringBoot默认整合了scheduled,使用步骤如下:

  • 在引导类加入@EnableScheduling注解,开启定时任务;

  • 在业务层方法上加入 @Scheduled注解,定义cron表达式周期执行。

业务层方法中开启的线程可以根据当前机器的配置来修改。我们这里开了7个线程,每个线程去执行20次循环,一次添加5000条数据。这里要注意mybatis批量插入时,不建议超过10000条错误。因为数据量过大,容易出现栈内存溢出的问题。

@Component
public class UserServiceImpl {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    //线程池
    private ThreadPoolExecutor executor;
    @Scheduled(cron = "0/20 * * * * ?") //每隔20秒执行一次
    public void  addList(){
        System.out.println("定时器被触发");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 7; i++) {

            Thread thread = new Thread(() -> {
                try {
                    for (int j = 0; j < 20; j++) {
                        userMapper.addList(UserUtil.getUsers(5000));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            try {
                executor.execute(thread);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
}

7.生成对象的util

我们用来模拟生成要插入的数据,实际业务开发的时候可以是从excel中导入的数据。

public class UserUtil {
    private static Random random = new Random();
    public static List<User> getUsers(int num){
        List<User> users = new ArrayList<>();
        for (int i = 0;i<num;i++){
            User user = new User();
            user.setBirthday(LocalDate.now());
            user.setSex(random.nextInt(2));
            user.setPwd("123"+random.nextInt(100000));
            user.setUsername("batch"+random.nextInt(num));
            users.add(user);
        }
        return users;
    }
}

8.线程池配置

线程池参数:

  • corePoolSize 核心线程数,在线程池中要保证的最小线程数;

  • mainumPoolSize 最大线程数,线程池中能运行的最大线程数;

  • keepAliveTime 保证存活时间,当线程空闲时间,多久会回收线程;

  • unit 和keepAliveTime配合使用,时间单位;

  • workQueue 工作队列,用于存储任务在任务被执行之前。

@Configuration
public class ThreadPoolExecutorConfig {
    @Bean
    public ThreadPoolExecutor threadPoolExecutor() {
        //线程池中6个线程,最大8个线程,用于缓存任务的阻塞队列数5个
        ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 8, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
        executor.allowCoreThreadTimeOut(true);//允许超时
        return executor;
    }
}

9.完整项目结构

10.测试

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

经典蓝牙连接过程

ACL是连接基础&#xff0c;sco需要再ACL连接成功后建立连接。基本链接流程如下: 一、创建连接 从用户点击扫描到的蓝牙设备开始&#xff0c;发起连接的设备会发送create_connection给controller。controller会进行page过程。 而后会底层链接完成&#xff0c;这个完成仅仅是确认…

CSS初级教程【第四天】

CSS初级教程【第四天】【1】CSS 外边距【2】CSS 外边距合并【3】所有 CSS 外边距属性【4】CSS 内边距【5】内边距和元素宽度【6】所有 CSS 内边距属性【7】CSS 高度和宽度【8】设置 CSS 尺寸属性CSS上回学习链接 CSS初级教程【第一天】 CSS初级教程【第二天】 CSS初级教程【第三…

windows本地安装openjdk环境

1讲一下为什么要选openjdk 先讲一下为什么小编在这里选择openjdk&#xff0c;而不是像绝大多数人一样选择jdk。那是因为&#xff0c;jdk在1.8的某个版本之后&#xff0c;就不是免费的版本了&#xff0c;不能免费用于商用。有过工作经验的就知道&#xff0c;公司一般对电脑上安装…

掌握这17张图,没人比你更懂RecyclerView的预加载

回顾上一篇文章&#xff0c;我们为了减少描述问题的维度&#xff0c;于演示之前附加了许多限制条件&#xff0c;比如禁用了RecyclerView的预拉取机制。 实际上&#xff0c;预拉取(prefetch)机制作为RecyclerView的重要特性之一&#xff0c;常常与缓存复用机制一起配合使用、共…

Go语言设计与实现 -- Mutex源码剖析

上图来自面向信仰编程 上图中&#xff0c;第一列为常见的同步原语&#xff0c;第二列为容器&#xff0c;第三列为互斥锁。 接下来我们来逐一介绍一下&#xff1a; Mutex 我们先来看一下sync.Mutex的结构体&#xff1a; type Mutex struct {// 当前互斥锁的状态state int32…

代码随想录算法训练营第一天 java : 704.二分查找法、27.移除算法

文章目录Leecode 704.二分查找题目连接&#xff1a;[Leecode 704.二分查找](https://leetcode.cn/problems/remove-element/)遇到的问题题目二分法的第一种写法 &#xff08;左闭右闭)第二种解法&#xff08;左闭右开 代码呈现&#xff09;Leecode 27.移除元素题目链接&#xf…

MyBatis【创建与使用】

MyBatis【创建与使用】&#x1f34e;一. MyBatis&#x1f352;1.1. MyBatis 是什么&#xff1f;&#x1f352;1.2 没有使用MyBatis时的操作流程&#x1f352;1.3 MyBatis的操作与数据库之间的流程&#x1f34e;二.创建MyBatis项目&#x1f352;2.1 idea创建&#x1f352;2.2 配…

【Git】一文带你入门Git分布式版本控制系统(撤销修改、删除文件)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

Debezium故障演练

1、搭建演练环境 postgresql及wal2json插件安装:https://blog.csdn.net/li281037846/article/details/128411222 kafka及kafka-connect安装&#xff0c;略 //添加debezium connector curl -i -X POST -H "Content-Type:application/json" -H "Accepted:applic…

Qt样式(qss)使用小结(软件换肤,比如暗黑模式)

1.背景&#xff1a; Qt style sheet&#xff08;qss&#xff09;跟前端技术一样&#xff0c;就是为了美化界面。关键是&#xff0c;太好用了。之前还为此写过一篇博客。 Qt样式&#xff08;qss&#xff09;手册小结_大橘的博客-CSDN博客 其中主要是记录如何获取手册细节。 …

6、GPIO输入按键检测(轮询检测)

目录 0x01、简介 0x02、硬件设计 0x03、编写函数 0x001、按键初始化 0x002、按键检测 0x003、按键led翻转 0x04、源程序下载地址 0x01、简介 本次实验主要实现按键控制LED灯。 由于机械按键在按下和抬起的时候会产生按键抖动&#xff0c;所以在设计的时候需要考虑如何消除抖…

Pytorch可视化特征图(代码 亲测可用)

2013年Zeiler和Fergus发表的《Visualizing and Understanding Convolutional Networks》 早期LeCun 1998年的文章《Gradient-Based Learning Applied to Document Recognition》中的一张图也非常精彩&#xff0c;个人觉得比Zeiler 2013年的文章更能给人以启发。从下图的F6特征&…

会议OA项目-首页

目录一、Flex布局简介什么是flex布局&#xff1f;flex属性学习地址&#xff1a;案例演示二、轮播图组件及mockjs三、会议OA小程序首页布局一、Flex布局简介 布局的传统解决方案&#xff0c;基于盒状模型&#xff0c;依赖 display属性 position属性 float属性 什么是flex布局…

简单有效的Mac内存清理方法,不用收藏也能记住

Mac电脑使用的时间越久&#xff0c;系统的运行就会变的越卡顿&#xff0c;这是Mac os会出现的正常现象&#xff0c;卡顿的原因主要是系统缓存文件占用了较多的磁盘空间&#xff0c;或者Mac的内存空间已满。如果你的Mac运行速度变慢&#xff0c;很有可能是因为磁盘内存被过度占用…

如何理解并记忆DataFrame中的Axis参数

当我们遇到有axis参数的方法时&#xff0c;脑子里的第一反应应该是&#xff1a;这个方法一定是沿着某一方向进行某种“聚合”或者“过滤”操作。在此场景下&#xff0c;Axis参数就是用来设定操作方向的&#xff1a;是垂直方向还是水平方向&#xff1f; axis0: 一行一行推进&…

【微服务架构实战】第1篇之API网关概述

1.网关概述 采用分布式、微服务的架构模式开发系统时&#xff0c;API 网关是整个系统中必不可少的一环。 1.1 没有网关会有什么问题&#xff1f; 在微服务架构模式下&#xff0c;1个系统会被拆分成多个微服务&#xff0c;如果每个微服务都直接暴露给调用方&#xff0c;会有以…

MySQL主键和唯一键的区别

主键和唯一键基本知识参考这篇文章 MySQL表的约束 &#xff0c;本篇文章主要是谈一谈主键和唯一键的区别从而更好的理解唯一键和主键。 在上篇文章中已经提到 主键&#xff1a; primary key 用来唯一的约束该字段里面的数据&#xff0c;不能重复&#xff0c;不能为空&#x…

vue父页面调用子页面及方法及传参,鼠标光标定位

项目场景&#xff1a; vue父页面调用子页面及方法 问题描述 vue中父界面调用子界面及方法时界面可以调用&#xff0c;但是调用方法的时候第一次报错&#xff0c;但是关掉界面再次重新打开就没问题了 原因分析&#xff1a; 在我之前添加鼠标指针定位的时候&#xff0c;如果在…

记录scoped属性的使用和引发的问题

背景 在对表格数据进行样式处理时&#xff0c;通过业务逻辑判断&#xff0c;进行对符合要求的表格填充背景色&#xff0c;没有符合预期的效果。反复排查校验代码和判断逻辑&#xff0c;都没有什么问题&#xff0c;可能还是样式上出现问题。再通过F12 选取元素对表格设置背景色时…

获取树形结构中,父节点下所有子/孙节点(递归方式)

获取树形结构中&#xff0c;父节点下所有子/孙节点&#xff08;递归方式&#xff09;1 树形结构&#xff08;TreeItem类&#xff09;2 测试代码&#xff08;main函数&#xff09;3 运行效果1 树形结构&#xff08;TreeItem类&#xff09; 这里通用型树形结构为TreeItem类&…