通过实现Callable接口,使用多线程分页查询导出数据,使用SXSSFWorkbook写数据

通过实现Callable接口,使用多线程分页查询导出数据,使用SXSSFWorkbook写数据

说明

导出大量数据到excel文件,文件格式为.xlsx,版本07年及以上,使用多线程查询数据库数据,将数据通过SXSSFWorkbook将数据写入excel文件;避免堆溢出

实现Callable接口的ThredQuery类

package com.oims.thread;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.oims.util.SpringContextUtil;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实现Callable接口类
 */
public class ThredQuery implements Callable<List> {

	/**
	* 通过ApplicationContext获取bean 
	*/
    private ApplicationContext ac = SpringContextUtil.getApplicationContext();

    /**
     * 业务接口
     */
    private ServiceImpl serviceImp = null;
    /**
     * 业务参数
     */
    private Map<String, Object> map = new ConcurrentHashMap<>();
    /**
     * 业务接口名称
     */
    private String InterfaceName;
    /**
     * 业务方法名称
     */
    private String methodName;

    /**
     * 当前页
     */
    private int currentPage;

    /**
     * 每页显示数量
     */
    private int num;

    /**
     * @param currentPage   当前页
     * @param num           每页数量
     * @param map           业务参数
     * @param InterfaceName 业务接口
     * @param methodName    业务方法
     */
    public ThredQuery(int currentPage, int num, Map map, String InterfaceName, String methodName) {
        this.map = map;
        this.InterfaceName = InterfaceName;
        this.methodName = methodName;
        this.currentPage= currentPage;
        this.num = num;
    }

    @Override
    public List call() throws Exception {
        //返回数据给Future
        map.put("startRow", (currentPage- 1) * num);
        map.put("limit", num);
        //获取业务接口bean
        serviceImp = (ServiceImpl) ac.getBean(InterfaceName);
        //需要调用的业务方法(方法名称,参数类型)
        Method idMethod = serviceImp.getClass().getMethod(methodName, Map.class);
        Map<String, Object> mapCoty = new HashMap<>();
        mapCoty.putAll(map);
        //调用业务方法查询数据,返回集合
        return  (List) idMethod.invoke(serviceImp, mapCoty);
    }
}

在call方法中,将在构造方法中传入的参数map对象重新创建新的对象并赋值,避免多线程情况下调用业务方法传对象值只使用最后一个线程所传值的原因(每个线程从构造方法传的参数不一样,如果在call方法使用传进来的基本数据类型参数不会出现问题,但是使用传进来非基本数据类型参数时,需要重新创建一个新的对象,将原对象的map 值重新赋值给新的对象mapCoty给业务接口调用才不会出现问题)。

controller中的方法

/**
     * 导出
     *
     * @param response
     * @param request
     */
    @RequestMapping("/exportResponse")
    public void exportResponse(HttpServletResponse response, HttpServletRequest request) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        long startTime = System.currentTimeMillis();    //开始时间
        System.out.println("strat execute time:===== " + startTime);
        String url = FileSource.getUrl();//系统根路径
        String srcPath = url + File.separator + "upload/test.xlsx";
        //通过读取文件的形式写入内容,主要是因为表头多行,及存在合并单元格
        XSSFWorkbook workbook2 = new XSSFWorkbook(srcPath);
        SXSSFWorkbook wb = new SXSSFWorkbook(workbook2, -1);//在内存中保持100行,超过100行将被刷新到磁盘
        SXSSFSheet sh = wb.getSheetAt(0); // 获取原有的sheet对象
        PeEntRegistration peEntRegistration = new PeEntRegistration();
        QueryWrapper queryWrapper = new QueryWrapper();
        peEntRegistration.setYesManage("是");
        queryWrapper.setEntity(peEntRegistration);
        int count = peEntRegistrationService.count(queryWrapper);//查询数据库需要导出的总数量
        //====
        Map<String, Object> map = new HashMap<>();
        map.put("yesManage", "是");
        List<BeRegInfoExportResult> peEntRegistrationList = new ArrayList<>(count);//接收返回结果
        int num = 5000;//一次查询多少条
        int times = count % num > 0 ? count / num + 1 : count / num; //需要查询的次数总页数
        int bindex = 1;//开始页数
        //Callable用于产生结果
        List<Callable<List>> tasks = new ArrayList<>();
        for (int i = 0; i < times; i++) {
            Callable<List> qfe = new ThredQuery(bindex, num, map, "peEntRegistrationService", "queryListBy");
            tasks.add(qfe);
            bindex++;
        }
        //定义固定长度的线程池  防止线程过多
        ExecutorService executorService = Executors.newFixedThreadPool(15);
        //Future用于获取结果
        List<Future<List>> futures = executorService.invokeAll(tasks);
        //处理线程返回结果
        if (futures != null && futures.size() > 0) {
            for (Future<List> future : futures) {
                peEntRegistrationList.addAll(future.get());
            }
        }
        executorService.shutdown();//关闭线程池
        long end = System.currentTimeMillis();
        System.out.println("线程查询数据用时:" + (end - startTime) / 1000 + "ms");
        System.out.println("finished execute  time:======== " + (System.currentTimeMillis() - startTime) / 1000 + "m");
        SXSSFRow row = null;
        SXSSFCell cell = null;
        for (int i = 0; i < peEntRegistrationList.size(); i++) {
            row = sh.createRow(i + 2);//从第三行开始写入数据
            cell = row.createCell(0);
            cell.setCellValue(peEntRegistrationList.get(i).getEnterpriseName());
            cell = row.createCell(1);
            cell.setCellValue(peEntRegistrationList.get(i).getRegistTime() != null ? sdf.format(peEntRegistrationList.get(i).getRegistTime()) : "");
            cell = row.createCell(2);
            cell.setCellValue(peEntRegistrationList.get(i).getRegistType());
            cell = row.createCell(3);
            cell.setCellValue(peEntRegistrationList.get(i).getForeignSources());
            cell = row.createCell(4);
            cell.setCellValue(peEntRegistrationList.get(i).getRegistCapital() + "");
            cell = row.createCell(5);
            cell.setCellValue(peEntRegistrationList.get(i).getIndustry());
            cell = row.createCell(6);
            cell.setCellValue(peEntRegistrationList.get(i).getBusinessSize());   
        }
        peEntRegistrationList.clear(); 
        FileOutputStream fileOut = new FileOutputStream(url + File.separator + "upload/test1.xlsx");
        wb.write(fileOut);
        fileOut.close();
        wb.dispose();
        long finishedTime = System.currentTimeMillis();    //处理完成时间
        System.out.println("finished execute  time:======== " + (finishedTime - startTime) / 1000 + "m");
        //下载文件
        FileSource.download("/upload/test1.xlsx", request, response);
    }

FileSource下载工具类

package com.oims.util;

import org.springframework.util.ResourceUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import java.util.Base64;

public class FileSource {
    
    /**
     * 下载
     *
     * @param url      文件夹及到文件的路径,不是全路径(例如:/upload/test.doc)
     * @param request
     * @param response
     * @throws Exception
     */
    public static void download(String url, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String path = getUrl();
        url = path + url;
        String fileName = url.substring(url.lastIndexOf(File.separator), url.length());
        download(url, fileName, request, response);
    }

    /**
     * 下载
     *
     * @param url      全文件路径(例如:D:/upload/test.doc)
     * @param name     新文件名
     * @param request
     * @param response
     * @throws Exception
     */
    public static void download(String url, String name, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String encodedFileName = name.trim().replaceAll(" ", "");
        String agent = request.getHeader("User-Agent");
        boolean isMSIE = ((agent != null) && (agent.toUpperCase().indexOf("MSIE") != -1));
        if (isMSIE) {
            encodedFileName = URLEncoder.encode(encodedFileName, "UTF-8");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器,此处使用java8
            encodedFileName = "=?utf-8?B?" + Base64.getEncoder().encodeToString(encodedFileName.getBytes("utf-8")) + "?=";
        } else {
            encodedFileName = new String(encodedFileName.getBytes("UTF-8"), "ISO8859-1");
        }
        response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
        File file = new File(url);
        FileInputStream is = new FileInputStream(file);
        ServletOutputStream os = response.getOutputStream();
        byte[] b = new byte[1024];
        int len = 0;
        try {
            while ((len = is.read(b, 0, 1024)) != -1) {
                os.write(b, 0, len);
            }
        } finally {
            is.close();
            os.flush();
        }
    }
	
	/**
	 * 获取项目的根路径,springboot项目打成jar或者war之后运行,文件只能存到项目外的地方
	 */
    public static String getUrl() {
        try {
            String url = new File(ResourceUtils.getURL("classpath:").getPath()).getParentFile().getParentFile().getParent();
            return url.replace("file:", "") + File.separator;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值