1.使用SXSSF,而不使用XSSF或HSSF。
HSSF 导出行数太少,不够用。
XSSF 导出行数多,但内存消耗大。
从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的API---- SXSSF
2.数据量大,使用多线程实现。
3.使用main方法方式测试。
三 项目
1.jar包+Myabtis配置文件创建
自己用到的jar包,可能有多余的,自行添加,下载链接如下。
链接:https://pan.baidu.com/s/1q3NTbxsJ5OlePc7pjj4gbA
提取码:ILYl
配置文件如下:
2.代码和mapper.xml 如下
mapper.xml
select c_1, c_2, c_3, c_4, c_5, c_6, c_7, c_8, c_9, c_10, c_11, c_12, c_13, c_14, c_15, c_16, c_17, c_18, c_19, c_20, c_21, c_22, c_23, c_24, c_25, c_26, c_27, c_28, c_29, c_30, c_31, c_32, c_33, c_34, c_35, c_36, c_37, c_38, c_39, c_40, c_41, c_42, c_43, c_44 from (select rownum rn, t.* from (select * from Co_ToExData_zfmx ctz where ctz.workflowno =#{workflowno}) t) WHERe RN BETWEEN #{starNum} AND #{endNum} and workflowno = #{workflowno}
vo类如下:
package excel; public class Co_ToExData_zfmx { //注,根据自己的实际情况调整,字段以及字段类型 private String c_1 ; private String c_2 ; private String c_3 ; private String c_4 ; private String c_5 ; private String c_6 ; private String c_7 ; private String c_8 ; private String c_9 ; private String c_10; private String c_11; private String c_12; private String c_13; private String c_14; private String c_15; private String c_16; private String c_17; private String c_18; private String c_19; private String c_20; private String c_21; private String c_22; private String c_23; private String c_24; private String c_25; private String c_26; private String c_27; private String c_28; private String c_29; private String c_30; private String c_31; private String c_32; private String c_33; private String c_34; private String c_35; private String c_36; private String c_37; private String c_38; private String c_39; private String c_40; private String c_41; private String c_42; private String c_43; private String c_44; //注,get,set方法此处省略,记得自己补充完整 }
实现类如下 ExcelListMore :
package excel; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.log4j.Logger; 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.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.tools.zip.ZipEntry;//在 ant.jar 里 有对应的zip包 import org.apache.tools.zip.ZipOutputStream; public class ExcelListMore { private static Logger logger = Logger.getLogger(ExcelListMore.class.getName()); // 思路 // 直接写出,单个 excel文件,1个 sheet页, 用 XLSX 方式写 // 多线程查库,最终结果整合,转为2维数组 // sheet页写入 单线程 *** 作 // 压缩包 // 删除 生成的 excel文件 public void exportExcel(String workflowno,SqlSession session) throws Exception { // 装载文件 FileOutputStream outputStream = null; // 创建一个工作簿 Workbook workbook = new SXSSFWorkbook(); // 真实数据条数,需要sql 查询 项目中的是mapper 形式获取 String selctCount = "excel.Co_ToExData_zfmxMapper.selectCountZFTZ";//映射sql的标识字符串 //查询总条数 int dataCount = (Integer) session.selectOne(selctCount, workflowno); // 校验数据!!! if (dataCount == 0) { throw new Exception("通知单生成excel,无结果集!"); } // excel写入 workbook工作簿,sizeData总数,workflowno 流水号, session 数据库连接 ;返回值 计划名 //session 最终需要删除 String planName = writeExcel(workbook, dataCount, workflowno,session); // 设置输出路径和 输出文件名 // 填写值,日期填写 String outStr = "D:/"; Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 生成文件名称 String outName = planName + sdf.format(date) + ".xlsx"; // 输出路径 String outPath = outStr + outName; try { outputStream = new FileOutputStream(outPath); // 写出excel // 怎么查看sheet里的数据行 int tnum = workbook.getSheetAt(0).getPhysicalNumberOfRows(); System.out.println("获取总共多少有数据的行数!!!!!!!!!!!!!!!!!!" + tnum); workbook.write(outputStream); } catch (Exception e) { logger.error("支付通知写出excel报错!", e); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } // 压缩方法,注意路径问题,需要调整 writeZip(outPath, outName); // 需要关闭流之后删除 File deleteFile = new File(outPath); deleteFile.delete(); } public String writeExcel(Workbook workbook, int dataCount, String workflowno,SqlSession session) { // 创建工作表 Sheet sheet = workbook.createSheet(); workbook.setSheetName(0, "支付通知"); // 创建表头 根据自己的实际情况调整 Row row = sheet.createRow(0); String[] heads = { "交易序列号", "交易类别", "支付流水号", "受托人年金计划编码", "年金计划名称", "企业年金计划登记号", "企业编码", "企业名称", "组织机构代码", "个人编码", "员工编号", "姓名", "证件类型", "证件号码", "社会保障号码", "未归属金额", "企业缴费归属比例", "企业缴费总个税金额", "支付类型", "支付方式", "支付周期", "支付期数", "是否为最后一期", "支付起始月份", "本次支付金额", "支付总金额", "受益人姓名", "受益人证件类型", "受益人证件号码", "受益人社会保障号码", "受益人开户银行", "受益人开户银行行号", "受益人开户银行所在省", "受益人开户银行所在市", "受益人账户户名", "受益人银行账号", "受益人支付金额", "应纳税额", "实际领取额", "摘要", "非投资赎回金额", "受托人申请编号", "估值日期", "受益人开户网点" }; Cell cell = null; for (int i = 0; i < heads.length; i++) { cell = row.createCell(i); cell.setCellValue(heads[i]); } // 假数据量!!!! 需要删除!!!!! // dataCount = 100001; // 初始化页大小 int pageSize = 0; if (dataCount < 50000) { pageSize = 25000; } else if (dataCount < 200000) { pageSize = 50000; } else { pageSize = 100000; } // 总页数 int pageCount = (dataCount + pageSize - 1) / pageSize; // 开启多线程,但是需要有返回值,总的结果集!!!! List三 总结和疑问exDatalist = new ArrayList (); // FutureTask的get方法是线程阻塞的,必须要等待线程都执行完成后,才能获取到结果 List futureTaskList = new ArrayList (); // 开始构建多线程 for (int page = 0; page < pageCount; page++) { // 开始starNum,结束 endNum int starNum = page * pageSize + 1; // 结束endNum;dataCount 数据总数 int endNum = ((page + 1) * pageSize < dataCount) ? (page + 1) * pageSize : dataCount; // Callable + FutureTask 实现多线程,返回值 MyCallable mainCall = new MyCallable(starNum, endNum, session, workflowno); FutureTask > mainFuture = new FutureTask
>(mainCall); // 为后面获取返回值做准备 futureTaskList.add(mainFuture); // 构建线程 Thread thread = new Thread(mainFuture, "下标为" + page + "的线程!"); thread.start(); } for (FutureTask> futureTask : futureTaskList) { // FutureTask的get方法是线程阻塞的,必须要等待线程都执行完成后,才能获取到结果 try { List
mainList = (List ) futureTask.get(); exDatalist.addAll(mainList); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 对象值转化成2维数组 String[][] tArr5 = listToArrayWay(exDatalist); // 起始行 为 1!!!!!!!!!! 如何写活 writeToSheet(sheet, 1, tArr5); // 返回计划名称,要考虑为空的情况么? return exDatalist.get(0).getC_5(); } // 压缩文件 outPath 文件路径 public void writeZip(String outPath, String zipname) { // 压缩包名字 OutputStream outputStream = null; // 装载文件 BufferedInputStream inputStream = null; try { outputStream = new BufferedOutputStream(new FileOutputStream("D:/" + zipname + ".zip")); } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } ZipOutputStream zipOut = new ZipOutputStream(outputStream); // 设置中文编码 zipOut.setEncoding("gbk"); try { File file = new File(outPath); // 定义要压缩的文件 // 需要的是一个字符串名字 ZipEntry entry = new ZipEntry(file.getName()); zipOut.putNextEntry(entry); inputStream = new BufferedInputStream(new FileInputStream(file)); int n; byte[] buffer = new byte[1024 * 10]; while ((n = inputStream.read(buffer)) != -1) { zipOut.write(buffer, 0, n); } zipOut.flush(); zipOut.closeEntry(); zipOut.close(); } catch (Exception e) { logger.error("压缩文件出错!", e); } finally { if (zipOut != null) try { zipOut.close(); } catch (IOException e) { e.printStackTrace(); } if (outputStream != null) try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } if (inputStream != null) try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } private void writeToSheet(Sheet sheet, int number, String[][] tArr5) { Row row = null; Cell cell = null; int tRow = tArr5.length;// 多少行 int tCol = tArr5[0].length; // 多少列 // 行开始 for (int i = 0; i < tRow; i++) { // 第二列值 row = sheet.createRow(i + number); for (int j = 0; j < tCol; j++) { cell = row.createCell(j);// 建立新cell cell.setCellValue(tArr5[i][j]); } } } // 结果集转化为2维数组!!! public String[][] listToArrayWay(List list) { Object o = list.get(0); String[] filedNames = getFiledName(o); int filedNum = filedNames.length; int listSize = list.size(); List methods = getGetField(filedNames, o); String[][] arrs = new String[listSize][filedNum]; int i = 0; for (Object object : list) { int j = 0; for (Method method : methods) { Object value = null; try { value = method.invoke(object, new Object[] {}); } catch (Exception e) { // TODO Auto-generated catch block System.out.println("属性不存在" + e); } arrs[i][j] = (String) value; j++; } i++; } return arrs; } // 获取obj属性 集合 private String[] getFiledName(Object o) { try { Field[] fields = o.getClass().getDeclaredFields(); String[] fieldNames = new String[fields.length]; for (int i = 0; i < fields.length; i++) { fieldNames[i] = fields[i].getName(); // System.out.println("===i:"+i+"======="+fields[i].getName()); } return fieldNames; } catch (SecurityException e) { e.printStackTrace(); System.out.println(e.toString()); } return null; } // 获取属性 method 集合 private List getGetField(String[] fieldNames, Object o) { List methods = new ArrayList (); for (String fieldName : fieldNames) { String firstLetter = fieldName.substring(0, 1).toUpperCase(); String getter = "get" + firstLetter + fieldName.substring(1); Method method = null; try { method = o.getClass().getMethod(getter, new Class[] {}); } catch (NoSuchMethodException e) { System.out.println("属性不存在"); continue; } // Object value = method.invoke(o, new Object[] {}); methods.add(method); } return methods; } private static class MyCallable implements Callable > { private Integer starNum;// 查询开始行 private Integer endNum;// 查询结束行 private SqlSession session;// 传的数据库连接 最终项目需要改动,是查库的连接 private String workflowno; // 工作流水号 @Override public List
call() throws Exception { //就是一个查库 *** 作!!! String selctList = "excel.Co_ToExData_zfmxMapper.selectZFTZByWorkflowno";//映射sql的标识字符串 Map parameters= new HashMap (); parameters.put("starNum",starNum); parameters.put("endNum",endNum); parameters.put("workflowno",workflowno); List list = session.selectList(selctList, parameters); // 查询结果存储在list内 // 返回查询结果 System.out.println("------------------观察是否是多线程查询,交错执行"); return list; } public MyCallable(Integer starNum, Integer endNum, SqlSession session, String workflowno) { super(); this.starNum = starNum; this.endNum = endNum; this.session = session; this.workflowno = workflowno; } } //main入口 public static void main(String[] args) throws Exception { //时间戳,开始时间 long start = System.currentTimeMillis(); //mybatis的配置文件 String resource = "conf.xml"; //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件) InputStream is = MybatisTest.class.getClassLoader().getResourceAsStream(resource); //构建sqlSession的工厂 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is); //创建能执行映射文件中sql的sqlSession SqlSession session = sessionFactory.openSession(); // 为了查库 把session 传进去了 ExcelListMore exportMor= new ExcelListMore(); exportMor.exportExcel("test0005", session); long end = System.currentTimeMillis(); long count = end - start; System.out.println("总用时间" + count); session.close(); } }
1.10w数据量写入,单线程 *** 作和多线程查库再写入 *** 作时间差别不大。效率基本一致。
2.单sheet页 多线程 查库+写入时,数据不能同步,受限代码
// 开始构建多线程 for (int page = 0; page < pageCount; page++) { // 开始starNum,结束 endNum int starNum = page * pageSize + 1; // 结束endNum;dataCount 数据总数 int endNum = ((page + 1) * pageSize < dataCount) ? (page + 1) * pageSize : dataCount; // Callable + FutureTask 实现多线程,返回值 MyCallable mainCall = new MyCallable(starNum, endNum, session, workflowno); FutureTask> mainFuture = new FutureTask
>(mainCall); // 为后面获取返回值做准备 futureTaskList.add(mainFuture); // 构建线程 Thread thread = new Thread(mainFuture, "下标为" + page + "的线程!"); thread.start(); }
原因是,for 内 new 了新类,再创建了线程类,导致锁不住。
故,变种为,多线程查库,结果集汇总,再单线程写入sheet页。
3.转为2维数组 *** 作excel写入时,比vo.getName()的方式快很多,不明实际原因。
**结语:**如有大牛解惑不剩感激。同时写给哪些奋斗的人,共勉。希望对一些人有所帮助。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)