基于java线程池和EasyExcel实现数据异步导入

基于java线程池和EasyExcel实现数据异步导入

2.代码实现

2.1 controller层

    @PostMapping("import")public void importExcel(MultipartFile file) throws IOException {importService.importExcelAsync(file);}

2.2 service层

@Resource
private SalariesListener salariesListener;private ExecutorService executorService = Executors.newFixedThreadPool(20);public void importExcelAsync(MultipartFile file) {// 开20个线程分别处理20个sheetList<Callable<Object>> tasks = new ArrayList<>();for (int i = 0; i < 20; i++) {int num = i;tasks.add(() -> {EasyExcel.read(file.getInputStream(), Salaries.class, salariesListener).sheet(num).doRead();return null;});}try {//等待所有任务完成executorService.invokeAll(tasks);} catch (InterruptedException e) {throw new RuntimeException(e);}}

2.3实体

@Data
@TableName("salaries")
public class Salaries {private Integer empNo;private Integer salary;private Date fromDate;private Date toDate;
}

2.4easyExcel 监听

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.domain.Salaries;
import com.woniu.mapper.SalariesMapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;@Component
public class SalariesListener extends ServiceImpl<SalariesMapper, Salaries> implements ReadListener<Salaries>, IService<Salaries> {private static final Log logger = LogFactory.getLog(SalariesListener.class);//创建一个线程池,用于异步保存数据private ExecutorService executorService = Executors.newFixedThreadPool(20);//创建一个线程安全的list,用于存储读取到的数据,使用ThreadLocal保证线程安全private ThreadLocal<ArrayList<Salaries>> salariesList = ThreadLocal.withInitial(ArrayList::new);//用于统计是第几次插入private static AtomicInteger count = new AtomicInteger(1);//设定需要异步批量插入的条数private static final int batchSize = 10000;@Resourceprivate SalariesListener salariesListener;@Override@Transactional(rollbackFor = Exception.class)public void invoke(Salaries data, AnalysisContext context) {//读取excel每一行的数据,添加到list中salariesList.get().add(data);//如果list的数据大于设定需要异步批量插入的条数,则执行异步插入if (salariesList.get().size() >= batchSize) {asyncSaveData();}}public void saveData() {if (!salariesList.get().isEmpty()) {saveBatch(salariesList.get(), salariesList.get().size());logger.info("第" + count.getAndAdd(1) + "次插入" + salariesList.get().size() + "条数据");salariesList.get().clear();}}public void asyncSaveData() {if (!salariesList.get().isEmpty()) {ArrayList<Salaries> salaries = (ArrayList<Salaries>) salariesList.get().clone();executorService.execute(new SaveTask(salaries, salariesListener));salariesList.get().clear();}}@Override@Transactional(rollbackFor = Exception.class)public void doAfterAllAnalysed(AnalysisContext context) {logger.info("一个Sheet全部处理完");//考虑每个sheet批量插入数据的条数少于异步插入的条数asyncSaveData();}//创建一个线程类,用于异步保存数据static class SaveTask implements Runnable {private List<Salaries> salariesList;private SalariesListener salariesListener;public SaveTask(List<Salaries> salariesList, SalariesListener salariesListener) {this.salariesList = salariesList;this.salariesListener = salariesListener;}@Overridepublic void run() {salariesListener.saveBatch(salariesList);//打印第几次插入,每次插入的数据logger.info("第" + count.getAndAdd(1) + "次插入" + salariesList.size() + "条数据");}}
}

2.5 建表语句

CREATE TABLE `salaries` (`emp_no` int(11) DEFAULT NULL COMMENT '员工号',`salary` int(11) DEFAULT NULL,`from_date` datetime DEFAULT NULL,`to_date` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
spring:servlet:multipart:max-request-size: 30MBmax-file-size: 1024MBdatasource:username: rootpassword: rooturl: jdbc:mysql://127.0.0.1:3306/llp?rewriteBatchedStatements=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Asia/Shanghaimain:allow-circular-references: true

3.测试验证

可以看到导入95万多条数据,耗时差不多在一份多钟

  • 导入开始时间

image-20250123102709029

  • 导入结束时间

image-20250123102727318

  • 入库数据

image-20250123102825151

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

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

相关文章

(15)Chainlink Automation(定时任务) 详细介绍及用法

Chainlink Automation 详细介绍 1. 什么是 Chainlink Automation&#xff1f; Chainlink Automation 是 Chainlink 提供的一个去中心化服务&#xff0c;专门用于自动化执行智能合约的链上操作。它允许开发者基于时间或特定条件&#xff08;如链上或链下事件&#xff09;触发智…

校验收货地址是否超出配送范围实战3(day09)

优化用户下单功能&#xff0c;加入校验逻辑&#xff0c;如果用户的收货地址距离商家门店超出配送范围&#xff08;配送范围为5公里内&#xff09;&#xff0c;则下单失败。 提示&#xff1a; ​ 1. 基于百度地图开放平台实现&#xff08;https://lbsyun.baidu.com/&#xff09…

Linux系统下速通stm32的clion开发环境配置

陆陆续续搞这个已经很久了。 因为自己新电脑是linux系统无法使用keil&#xff0c;一开始想使用vscode里的eide但感觉不太好用&#xff1b;后面想直接使用cudeide但又不想妥协&#xff0c;想趁着这个机会把linux上的其他单片机开发配置也搞明白&#xff1b;而且非常想搞懂cmake…

Spark SQL中的from_json函数详解

Spark SQL中的from_json函数详解 在Spark SQL中&#xff0c;from_json是一个用于解析JSON数据的函数&#xff0c;主要用于将JSON格式的字符串解析为结构化的数据&#xff08;即StructType或其他Spark SQL数据类型&#xff09;。这个函数在处理半结构化数据&#xff08;如JSON日…

leetcode_3092. 最高频率的 ID

https://leetcode.cn/problems/most-frequent-ids/description/ 看到这个数据范围 最极端情况 如果nums全为一个数 并且数量取到最大 那么范围是10的10次方 需要longlong储存 这题主要运用了哈希表配合multiset实现 哈希表主要用作存储某个数的出现次数 mst则用于记录出现次…

【深度学习】 自动微分

自动微分 正如上节所说&#xff0c;求导是几乎所有深度学习优化算法的关键步骤。 虽然求导的计算很简单&#xff0c;只需要一些基本的微积分。 但对于复杂的模型&#xff0c;手工进行更新是一件很痛苦的事情&#xff08;而且经常容易出错&#xff09;。 深度学习框架通过自动…

如何把jupyter的一个.ipynb文件的多个单元格cell合并为1个cell

1 jupyter的一个.ipynb文件的多个单元格cell合并为1个cell 步骤 1&#xff1a;打开 your_notebook.ipynb 文件 启动 Jupyter Notebook。 导航到你的工作目录&#xff08;例如 F:\main&#xff09;。 打开 your_notebook.ipynb 文件。 步骤 2&#xff1a;选择所有单元格 点击…

集成Sleuth实现链路追踪

文章目录 1.新增sunrays-common-cloud模块1.在sunrays-framework下创建2.pom.xml3.查看是否被sunrays-framework管理 2.创建common-cloud-sleuth-starter1.目录结构2.pom.xml3.sunrays-dependencies指定cloud版本4.SleuthAutoConfiguration.java5.spring.factories 3.创建commo…

AIP-127 HTTP和gRPC转码

编号127原文链接AIP-127: HTTP and gRPC Transcoding状态批准创建日期2019-08-22更新日期2019-08-22 遵守面向资源设计的API使用RPC进行定义&#xff0c;但面向资源设计框架允许这些API表现为整体上符合REST/JSON约定的接口。这一点很重要&#xff0c;可以帮助开发者利用现有知…

WPF基础 | 初探 WPF:理解其核心架构与开发环境搭建

WPF基础 | 初探 WPF&#xff1a;理解其核心架构与开发环境搭建 一、前言二、WPF 核心架构2.1 核心组件2.2 布局系统2.3 数据绑定机制2.4 事件处理机制 三、WPF 开发环境搭建3.1 安装 Visual Studio3.2 创建第一个 WPF 应用程序 结束语优质源码分享 WPF基础 | 初探 WPF&#xff…

字节跳动自研HTTP开源框架Hertz简介附使用示例

字节跳动自研 HTTP 框架 Hertz Hertz 是字节跳动自研的高性能 HTTP 框架&#xff0c;专为高并发、低延迟的场景设计。它基于 Go 语言开发&#xff0c;结合了字节跳动在微服务架构中的实践经验&#xff0c;旨在提供更高效的 HTTP 服务开发体验。 1. 背景介绍 随着字节跳动业务…

实战演示:利用ChatGPT高效撰写论文

在当今学术界&#xff0c;撰写论文是一项必不可少的技能。然而&#xff0c;许多研究人员和学生在写作过程中常常感到困惑和压力。幸运的是&#xff0c;人工智能的快速发展为我们提供了新的工具&#xff0c;其中ChatGPT便是一个优秀的选择。本文将通过易创AI创作平台&#xff0c…

在线可编辑Excel

1. Handsontable 特点&#xff1a; 提供了类似 Excel 的表格编辑体验&#xff0c;包括单元格样式、公式计算、数据验证等功能。 支持多种插件&#xff0c;如筛选、排序、合并单元格等。 轻量级且易于集成到现有项目中。 具备强大的自定义能力&#xff0c;可以调整外观和行为…

Vue.js 渐进式增强:如何逐步为传统项目注入活力

Vue.js 是一个渐进式框架&#xff0c;这意味着你可以将它逐步引入到现有项目中&#xff0c;而无需彻底重构。渐进式增强特别适合那些已经在使用传统服务器渲染框架&#xff08;如 PHP、Django、Laravel&#xff09;的项目&#xff0c;为它们增加动态交互功能。本篇教程将介绍如…

重构(4)

&#xff08;一&#xff09;添加解释性变量&#xff0c;使得代码更容易理解&#xff0c;更容易调试&#xff0c;也可以方便功能复用 解释性的变量 总价格为商品总价&#xff08;单价*数量&#xff09;-折扣&#xff08;超过100个以上的打9折&#xff09;邮费&#xff08;原价的…

某金融科技公司内腾讯专有云企业版TCE结合宁盾2FA双因子认证满足商用密码应用安全性评估

项目背景 金融行业数据安全至关重要&#xff0c;涉及国家经济安全和公民个人信息安全。监管机构为了确保金融体系的安全稳定&#xff0c;对金融机构的密码应用提出了严格要求。例如&#xff0c;国务院办公厅早在 2014 年发布的《金融领域密码应用指导意见》明确指出&#xff0c…

Seata进阶全文详解(集成Nacos及SpringCloud配置)

Seata 简介 在当今微服务架构盛行的时代&#xff0c;分布式系统中的事务管理成为了一个极具挑战性的问题。Seata&#xff08;Simple Extensible Autonomous Transaction Architecture&#xff09;应运而生&#xff0c;它是一款专为微服务架构设计的分布式事务解决方案&#xf…

spring-springboot -springcloud

目录 spring: 动态代理: spring的生命周期(bean的生命周期): SpringMvc的生命周期: SpringBoot: 自动装配: 自动装配流程: Spring中常用的注解&#xff1a; Spring Boot中常用的注解&#xff1a; SpringCloud: 1. 注册中心: 2. gateway(网关): 3. Ribbon(负载均…

STM32更新程序OTA

STM32的OTA&#xff08;Over-The-Air&#xff09;更新程序是一种通过无线通信方式&#xff0c;为设备分发新软件、配置甚至更新加密密钥的技术。以下是关于STM32 OTA更新程序的详细介绍&#xff1a; 一、OTA升级流程 STM32的OTA升级流程通常包括以下几个关键步骤&#xff1a;…

DAY9,递归实现计算 :1 + 1/3 - 1/5 + 1/7 - 1/9 + .... 1/n 的值

题目 用递归实现计算 :1 1/3 - 1/5 1/7 - 1/9 .... 1/n 的值&#xff0c;n通过键盘输入 思路 递进阶段&#xff1a;n、...... 、9、7、5、3、1 函数出口&#xff1a;递进到1 开始返回&#xff1b;函数返回值视为“总和” 回归阶段&#xff1a;对当前n取倒数&#xff1b;“总…