在前文里談過一次性從數據庫取一個大結果集有可能導致outofMemory,當時的想法是分批去取回來,今天把它實現了,特地把代碼分享出來:
工程下載:https://files.cnblogs.com/files/xiandedanteng/CsvDownload20191027.rar
生成CSV文件的三個不同函數
package com.hy.csvdld.util; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.List; import org.apache.log4j.Logger; import com.hy.csvdld.Entity.Emp; import com.hy.csvdld.service.EmpService; // 用於生成CSV文件 public class CsvMaker { private static Logger log = Logger.getLogger(CsvMaker.class); public void makeTenCsv(File file, EmpService empService) { try { List<Emp> emps = empService.selectTenEmp(); FileWriter fileWriter = new FileWriter(file, true); int index = 0; for (Emp emp:emps) { index++; String info =""+index+","+ emp.asCsvLine()+ System.getProperty("line.separator"); fileWriter.write(info); } fileWriter.flush(); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } }
// 整體下載方案 public void makeManyCsv(File file, EmpService empService,int count) { try { List<Emp> emps = empService.selectMany(count); FileWriter fileWriter = new FileWriter(file, true); int index = 0; for (Emp emp:emps) { index++; String info =""+index+","+ emp.asCsvLine()+ System.getProperty("line.separator"); fileWriter.write(info); } fileWriter.flush(); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } // 當count過大時,分批下載 public void makePartialCsv(File file, EmpService empService,int count) { try { int PartialSize=10000; int times=count/PartialSize; for(int i=0;i<times;i++){ log.info("第"+i+"批次處理"); FileWriter fileWriter = new FileWriter(file, true); List<Emp> emps = empService.selectPartial(i*PartialSize, PartialSize); int index = i*PartialSize; for (Emp emp:emps) { index++; String info =""+index+","+ emp.asCsvLine()+ System.getProperty("line.separator"); fileWriter.write(info); } fileWriter.flush(); fileWriter.close(); } } catch (IOException e) { e.printStackTrace(); } } }
具體SQL實現:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.hy.csvdld.dao.EmpMapper"> <select id="selectTenEmp" resultType="com.hy.csvdld.Entity.Emp"> select id,name,age,cdate as ctime from emp order by id limit 10 </select>
<!-- 一次性取夠 --> <select id="selectManyEmp" resultType="com.hy.csvdld.Entity.Emp"> select id,name,age,cdate as ctime from emp order by id limit #{count} </select>
<!-- 分批多次取 --> <select id="selectPartialEmp" resultType="com.hy.csvdld.Entity.Emp"> select id,name,age,cdate as ctime from emp order by id limit #{start},#{size} </select> </mapper>
程序的優化,歸根結底是兩條路:空間換時間 或是 時間換空間,本文的分批取做法,是時間換空間的路數。
--END-- 2019年10月27日16:06:54