通过Mybatis的流式查询ResultHandler解决超大Excel导出内存溢出问题 您所在的位置:网站首页 java写入excel最大条数 通过Mybatis的流式查询ResultHandler解决超大Excel导出内存溢出问题

通过Mybatis的流式查询ResultHandler解决超大Excel导出内存溢出问题

2023-09-28 11:04| 来源: 网络整理| 查看: 265

在项目开发过程导出Excel为常用功能,之前的一篇Java导出超大Excel文件,防止内存溢出已经解决了Excel写入层面时的内存问题,但数据库查询层面,仍存在由于默认的mybatis查询是将所有数据都查询到本地内存,因此仍有可能会导致内存溢出,因此本文再详细介绍记录通过mybatis的ResultHander进行流式查询读取来完全解决excel的大量数据导出内存溢出问题。

一.先批量插入测试数据 1.建表,包含2个字段username,age CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '姓名', `age` int(11) NOT NULL COMMENT '年龄', PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `uk_code_key`(`username`, `age`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; 2.UserMapper.xml insert into t_user(username,age) values (#{item.username},#{item.age}) 3.UserMapper.java int batchInsert(List list); 4.UserService.java & UserServiceImpl.java int batchInsert(List list);

在这里插入图片描述

5.UserController.java中编写测试代码,写入100万行数据 //1.插入1000101行测试数据 //http://localhost:8080/test/user/batchInsert @RequestMapping(value="/batchInsert", method = RequestMethod.GET) public String batchInsert(){ logger.info("method starting..."); long startTime = System.currentTimeMillis(); //每次批量插入2000条记录,提高插入效率 int batchSize = 2000; List list = new LinkedList(); for(int i=0;i0 && list.size() % batchSize == 0) { userService.batchInsert(list); logger.info("has batchInsert size: {}", i); list.clear();//清除list } } long endTime = System.currentTimeMillis(); logger.info("method finished,total spend time: {} ms.",(endTime-startTime)); return "batchInsert"; } 二、ResultHandler流式查询导出

ResultHandler接口可以用于进行流式查询(即一行一行从数据库中读取处理,因此不会占用本地内存),本文的核心就是通过调用mapper的方法,传入一个ResultHandler,然后在实现的方法中读取数据,然后一行一行处理。

1.在UserMapper.xml中配置

其中的resultSetType为FORWARD_ONLY,fetchSize为-2147483648

select id,username,age from t_user 2.UserMapper.java中编写方法 /**导出,mapper的方法需要是void返回,并且参数中含ResultHandler(流式查询遍历的条件),这里我没加参数,可以加上你的条件参数*/ public void export(ResultHandler resultHandler);

重要注意点:要满足流式查询,需要方法返回值为void,并且方法中有ResultHandler类型的参数。在mybatis源码中的MapperMethod.java中也能看到对应的代码判断如下: 在这里插入图片描述

3.自定义一个ExcelResultHandler,提供给所有导出的代码共用

此ResultHandler实现了excel的导出,并遍历mapper查询数据,一行一行写入excel,节省内存,可以在导出的业务代码进行使用,具有通用性,只需要new出对象然后调用相应的方法。

package cn.gzsendi.modules.framework.utils; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletResponse; import org.apache.ibatis.session.ResultContext; import org.apache.ibatis.session.ResultHandler; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import cn.gzsendi.modules.framework.reflect.Reflector; import cn.gzsendi.modules.framework.reflect.reflectasm.MethodAccessor; public abstract class ExcelResultHandler implements ResultHandler{ private final Logger logger = LoggerFactory.getLogger(this.getClass()); private AtomicInteger currentRowNumber = new AtomicInteger(0);//记录当前excel行号,从0开始 private Sheet sheet = null; private List headerArray ; //excel表头 private List fieldArray ; //对应的字段 //定义totalCellNumber变量, private int totalCellNumber; //定义导出成zip格式的还是原始的xlsx格式 private boolean isExportZip = true; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //定义要导出的excel文件名,不带xlsx后缀,默认为uuID,也可以通过构造函数传进来进行改变。 private String exportFileName = UUID.randomUUID().toString().replace("-", ""); public ExcelResultHandler(List headerArray,List fieldArray){ this.headerArray = headerArray; this.fieldArray = fieldArray; this.totalCellNumber = headerArray.size(); } public ExcelResultHandler(List headerArray,List fieldArray,boolean isExportZip){ this(headerArray,fieldArray); this.isExportZip = isExportZip; } public ExcelResultHandler(List headerArray,List fieldArray,String exportFileName){ this(headerArray,fieldArray); this.exportFileName = exportFileName; } public ExcelResultHandler(List headerArray,List fieldArray,String exportFileName,boolean isExportZip){ this(headerArray,fieldArray,exportFileName); this.isExportZip = isExportZip; } //出象方法,提供给子类进行实现,遍历写入数据到excel public abstract void tryFetchDataAndWriteToExcel(); public void handleResult(ResultContext


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有