首页 > 编程语言 >JAVA解析Excel文件 + 多线程 + 事务回滚

JAVA解析Excel文件 + 多线程 + 事务回滚

时间:2023-11-17 15:23:15浏览次数:39  
标签:uploadId JAVA String Excel resultList 线程 TFileInfoDo new 多线程

1. 项目背景:

客户插入Excel文件,Ececel文件中包含大量的数据行和数据列,单线程按行读取,耗时大约半小时,体验感不好。

思路:先将excel文件按行读取,存入List,然后按照100均分,n=list.szie()/100 + 1; n就是要开启的线程总数。(实际使用的时候,数据库连接池的数量有限制,n的大小要结合数据库连接池的数量做合理设置,因为没一个线程都要获取数据库连接操作数据库)

 

2.  主线程代码

 

CountDownLatch以及AtomicBoolean请网上自行查看。
    public UploadFileResultVo readExcel(MultipartFile file, String userName, String uploadId, String dataFileId) throws Exception {
UploadFileResultVo result = new UploadFileResultVo();
if(excelUtils.isValidNewProductExcel(file)){
Workbook workBook = excelUtils.getWorkBook(file);
log.info("uploadId:{} for file:{}",uploadId,file.getOriginalFilename());
Sheet sheet = workBook.getSheetAt(0);
int firstRowNum = sheet.getFirstRowNum();
int lastRowNum = sheet.getLastRowNum();
// 获取厂家信息中所有的行
List<Row> factoryRows = new ArrayList<>();
for(int i = firstRowNum + 1; i <= lastRowNum; i++ ){
Row row = sheet.getRow(i);
factoryRows.add(row);
}
log.info("factoryRows:{}", factoryRows.size());
// 用户上传的内容为空
if(factoryRows.isEmpty()){
log.info("the new product data file is empty");
String filePath = fileUtils.ossUploadSignalFile(file, FileTypeEnum.NEW_PRODUCT_FILE.getCode());
TFileInfoDo TFileInfoDo = new TFileInfoDo();
TFileInfoDo.setFilePath(filePath);
TFileInfoDo.setUploadId(uploadId);
TFileInfoDo.setCreateBy(userName);
TFileInfoDo.setDataFileId(dataFileId);
TFileInfoDo.setFileType(FileTypeEnum.NEW_PRODUCT_DATA_FILE.getCode());
TFileInfoMapper.insertTFileInfo(TFileInfoDo);
Long fileId = TFileInfoDo.getId();
FileBackBo fileBackBo = new FileBackBo();
int lastIndex = filePath.split("/").length - 1;
String fileName = filePath.split("/")[lastIndex];
fileBackBo.setFileId(String.valueOf(fileId));
fileBackBo.setName(fileName);
List<FileBackBo> list = new ArrayList<>();
list.add(fileBackBo);
result.setUploadId(uploadId);
result.setDataFileId(dataFileId);
result.setFiles(list);
return result;
}
// 厂家信息中每100行放入一个list,开启一个线程分析数据
List<List<Row>> resultList = averageAssign(factoryRows, 100);
log.info("size:{}", resultList.size());
// 获取最后合计行
// Row lastRow = sheet.getRow(lastRowNum);
// 子线程个数(包含最后一行数据分析线程)
// CountDownLatch sonThreadNumber = new CountDownLatch(resultList.size() + 1);
CountDownLatch sonThreadNumber = new CountDownLatch(resultList.size());
log.info("sonThreadNumber:{}", sonThreadNumber.getCount());
// 子线程回滚标志
AtomicBoolean rollBack = new AtomicBoolean(false);
// 开启分析工厂数据子线程,此处不需要用线程池
for(int threadNumber = 0; threadNumber < resultList.size(); threadNumber++){
GetRowContent getRowContent = new GetRowContent(resultList.get(threadNumber), uploadId, dataFileId, sonThreadNumber, rollBack, "factoryDataAnalysisThread" + threadNumber, userName);
Thread thread = new Thread(getRowContent);
thread.start();
}
// 分析最后一行数据子线程
// GetLastRowContent getLastRowContent = new GetLastRowContent(lastRow, uploadId, sonThreadNumber, rollBack, "lastRowDataAnalysisThread", userName);
// Thread thread = new Thread(getLastRowContent);
// thread.start();
// 主线程等待所有子线程数据分析完毕
sonThreadNumber.await();
if(rollBack.get()){
log.info("子线程执行有异常");
throw new RuntimeException("数据解析出错,请检查录入文件内容格式是否按照模板录入");
}
else {
log .info("所有子线程执行成功");
//文件存放
String filePath = fileUtils.ossUploadSignalFile(file, FileTypeEnum.NEW_PRODUCT_FILE.getCode());
TFileInfoDo TFileInfoDo = new TFileInfoDo();
TFileInfoDo.setFilePath(filePath);
TFileInfoDo.setUploadId(uploadId);
TFileInfoDo.setCreateBy(userName);
TFileInfoDo.setDataFileId(dataFileId);
TFileInfoDo.setFileType(FileTypeEnum.NEW_PRODUCT_DATA_FILE.getCode());
TFileInfoMapper.insertTFileInfo(TFileInfoDo);
Long fileId = TFileInfoDo.getId();
FileBackBo fileBackBo = new FileBackBo();
int lastIndex = filePath.split("/").length - 1;
String fileName = filePath.split("/")[lastIndex];
fileBackBo.setFileId(String.valueOf(fileId));
fileBackBo.setName(fileName);
List<FileBackBo> list = new ArrayList<>();
list.add(fileBackBo);
result.setUploadId(uploadId);
result.setDataFileId(dataFileId);
result.setFiles(list);
}
}
return result;
}

3. 子线程
子线程继承Runnable接口,这种实现不能将参数传递给run()方法。可以在子线程类的构造方法中将CountDownLatch、AtomicBoolean以及其它参数作为属性变量注入,这样在run()方法中直接调用属性变量。

 run()方法

 

public class GetRowContent implements Runnable{

private final List<Row> resultList;
private final String uploadId;
private final String dataFileId;
private final CountDownLatch sonCountDownLatch;
private final AtomicBoolean rollBack;
private final String threadName;
private final String userName;

public GetRowContent(List<Row> resultList, String uploadId, String dataFileId, CountDownLatch sonCountDownLatch,AtomicBoolean rollBack, String threadName, String userName){
this.resultList = resultList;
this.uploadId = uploadId;
this.dataFileId = dataFileId;
this.sonCountDownLatch = sonCountDownLatch;
this.rollBack = rollBack;
this.threadName = threadName;
this.userName = userName;
}


@Override
public void run() {
// 其它线程已经报错,退出
if(rollBack.get()){
sonCountDownLatch.countDown();
return;
}
// 设置一个事务
PlatformTransactionManager transactionManager = (PlatformTransactionManager) SpringContextUtils.getBean("transactionManager");
GetRowContentService getRowContentService = (GetRowContentService) SpringContextUtils.getBean("getRowContentServiceBean");
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 事物隔离级别,开启新事务,这样会比较安全些
TransactionStatus status = transactionManager.getTransaction(def);
try{
getRowContentService.analysisData(resultList,uploadId, dataFileId, userName);
} catch(Exception e){
log.info("线程:{},执行异常",threadName);
e.printStackTrace();
rollBack.set(true);
}
finally{
sonCountDownLatch.countDown();
}
try{
sonCountDownLatch.await();
if(rollBack.get()){
log.info("线程:{},有子线程执行失败,进入回滚", threadName);
transactionManager.rollback(status);
}
else {
log .info("线程:{},执行成功,提交数据", threadName);
transactionManager.commit(status);
}

} catch(Exception e){
log.info("线程:{},发令枪异常,进入回滚", threadName);
transactionManager.rollback(status);
}
}
}

 

标签:uploadId,JAVA,String,Excel,resultList,线程,TFileInfoDo,new,多线程
From: https://www.cnblogs.com/andy1234/p/17838840.html

相关文章

  • JAVA 解析Excel + 多线程 + 事务回滚(2)
    该方法为网上查询,感觉可行,并未真正尝试。主线程:packagecom.swagger.demo.service;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importcom.swagger.demo.config.SpringJobBeanFactory;importcom.swagger.demo.m......
  • Java实现学生类继承自human类
    编写人类Human,包括私有姓名、性别、年龄,定义获取各个字段的公共方法,再定义公共的构造方法和思考方法。编写继承人类的学生类Student,增加私有的学号字段以及公共的获取学号的方法,还有公共的构造方法、学习方法,并重写toString方法获取学生数据。最后定义测试类,构造若干个学生对象......
  • Java生成随机三维数组图片推流
    生成随机的三维数组frame=np.random.randint(1,254,size=(720,1280,3)).astype(np.uint8)三维数组图片添加时间frame=cv2.putText(frame,datetime.datetime.now().__str__(),(100,100),cv2.FONT_HERSHEY_SIMPLEX,2.0,(255,255......
  • Java代码混淆
    classFinal代码混淆1.参数说明-file加密的jar/war完整路径-packages加密的包名(可为空,多个用","分割)-libjarsjar/war包lib下要加密jar文件名(可为空,多个用","分割)-cfgfiles需要加密的配置文件,一般是classes目......
  • java RestTemplate 发送post请求
    RestTemplate简介RestTemplate是执行HTTP请求的同步阻塞式的客户端,它在HTTP客户端库(如JDKHttpURLConnection,ApacheHttpComponents,okHttp等)基础封装了更加简单易用的模板方法API。即RestTemplate是一个封装,底层的实现还是java应用开发中常用的一些HTTP客户端。相对于直接使用底层......
  • java中的异步任务处理和Feature接口
    简介Java并发包提供了一套框架,大大简化了执行异步任务所需要的开发。框架引入了“执行服务”的概念,封装了任务执行的细节,对任务提交者而言,他可以关注任务本身,如提交任务、获取结果、取消任务。而不用关注任务执行的细节。基本接口①Runnable和Callable:表示要执行的任务②Excecuto......
  • javascript postMessage给子页面发消息
    发送消息页面<!DOCTYPEhtml><html><head><title>demo</title><metacharset="utf-8"/><script>varchildwinconstchildname="popup"functionopenChild(){......
  • java如何做大体积的文件上传和下载
    在Java中,实现大体积文件的上传和下载涉及到处理文件的分片、并发上传、断点续传等问题。本文将详细介绍如何通过Java实现大体积文件的上传和下载。1.文件上传文件上传是将本地文件上传到服务器的过程。对于大体积文件的上传,我们可以将文件分成多个小片段进行并发上传。1.1文件分......
  • 【Java基础】Java中switch的多种写法
    Java中switch的多种写法代码需求:键盘录入一个数字(代表星期几),判断是工作日还是休息日switch最基础写法 publicstaticvoidswitchTest(){while(true){System.out.println("请输入:");Scannersc=newScanner(System.in);......
  • 35个超实用excel快捷键
    以下是一些常用的Excel快捷键,希望对你有所帮助。如果你想要了解更多快捷键,可以参考Excel的官方文档或者在网上搜索相关信息。Ctrl+C:复制选定的单元格或单元格范围。Ctrl+X:剪切选定的单元格或单元格范围。Ctrl+V:粘贴复制或剪切的内容。Ctrl+Z:撤销上一步操作。Ctrl+......