采用JDBC的設計模式開發導出數據表格為excel表功能
1、需求分析
- 提供統一的控制器Controller,統一的生成Excel表的Service方法。
- 每個模塊的開發者根據自身業務需求組裝excel的表頭和表身數據以Map(表頭數據)和JSONArray組裝的JSONObject(表身數據)
2、實現思想
- 控制器接受各個模塊的實現類、page、rows、sort等基本分頁信息
- service中使用反射Class.forName將模塊實現類加載到JVM中,隨后取出實現類的數據交給生成Excel方法進行excel表格文件生成。
3、實現代碼以及步驟
- 聲明一個接受實現類名,page、row、sort等屬性的實體類
public class PrintVo {
@NotNull
public String className;
public Integer page;
public Integer rows;
public String sort;
public String order;
public String filterRules;
// 節省篇幅不寫getter/setter方法
}
- 聲明一個獲取數據的接口
public interface GetDataForExcel {
public ExcelDataVo getData(PrintVo print);
}
- 用于獲取對應的實現類的manager
public class GetDataForExcelManager {
public static GetDataForExcel getDataForExcel = null;
// 注冊獲取excel方法信息
public static void registerGetDataForExcel(GetDataForExcel data) {
getDataForExcel = data;
}
// 獲取excel方法
public static GetDataForExcel getGetDataForExcel() {
return getDataForExcel;
}
}
- 控制器層接受參數并調用service
public Result print(PrintVo printVo, HttpServletResponse response) {
if (printVo.getClassName() == null) {
return new Result(false, -1, "打印失敗","打印參數有誤","打印參數有誤");
}
InputStream in = exportExcelService.generate(printVo);
if (in == null) {
return new Result(false, -1, "導出失敗", "導出表格失敗", "導出失敗,請稍后重新嘗試");
}
response.setContentType("application/vnd.ms-excel");
try {
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("表格導出.xlsx", "UTF-8"));
FileCopyUtils.copy(in, response.getOutputStream());
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 這句代碼執行不到咯
return new Result(false, -1, "導出成功", "導出表格成功", "導出成功");
}
- Service層使用Class.forName加載實現類到虛擬機中并獲取數據執行生成excel操作
public InputStream generate(PrintVo printVo) {
try {
// 1、反射調用對應的實現類
Class.forName(printVo.getClassName());
GetDataForExcel data = GetDataForExcelManager.getGetDataForExcel();
// 2、傳參到對應方法并獲取表頭表身信息
ExcelDataVo excelData = data.getData(printVo);
InputStream in = toExcel("POITest", "table export Excel", excelData.getHead(), excelData.getBody());
return in;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
- 以下是舉例某一模塊的代碼,也就是不管有多少模塊,都只需要遵循實現這個接口,并注冊一個manager靜態類即可
@Service
public class ContractMainPrintImpl implements GetDataForExcel{
@Autowired
private ContractMaintenanceService mContractMaintenanceService;
@Override
public ExcelDataVo getData(PrintVo print) {
PageVo vo = new PageVo();
vo.setPage(print.getPage());
vo.setRows(print.getRows());
vo.setSort(print.getSort());
vo.setOrder(print.getOrder());
// 這些代碼是為了調用其它的service查詢對應的數據
List<Map> queryList = new ArrayList<Map>();
String rules = print.getFilterRules();
if(StringUtils.isNotBlank(rules)){
ObjectMapper mapper = new ObjectMapper();
try {
queryList = (List<Map>)mapper.readValue(rules, queryList.getClass());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
PageInfo<ContractMaintenanceVo> contractPage = mContractMaintenanceService.listAllPage(vo, queryList);
// 組裝表頭
Map<String, String> heads = new HashMap<>();
heads.put("0", "表頭1");
heads.put("1", "表頭2");
heads.put("2", "表頭3");
heads.put("3", "表頭4");
heads.put("4", "表頭5");
// 組裝表身
JSONArray bodys = new JSONArray();
List<ContractMaintenanceVo> contractMaintenances = contractPage.getList();
contractMaintenances.stream().forEach(c -> {
JSONObject jsonObject = new JSONObject();
jsonObject.put("0", c.getInstitutionName());
jsonObject.put("1", c.getAccountingCode());
jsonObject.put("2", c.getInstitutionCode());
jsonObject.put("3", c.getLicenseRegistrationCode());
jsonObject.put("4", c.getLnstitutionalLevel());
bodys.add(jsonObject);
});
ExcelDataVo excelData = new ExcelDataVo();
excelData.setType("ContractMaintenanceVo");
excelData.setBody(bodys);
excelData.setFileName("合同簽約主體信息表");
excelData.setHead(heads);
return excelData;
}
}
// 在spring啟動的時候會將對應實現類注入進來
@Component
public class ContractMainPrintManager {
private static ContractMainPrintImpl mContractMainPrintImpl;
@Autowired
public void setContractMainPrintImpl(ContractMainPrintImpl contractMainPrintImpl) {
ContractMainPrintManager.mContractMainPrintImpl = contractMainPrintImpl;
GetDataForExcelManager.registerGetDataForExcel(mContractMainPrintImpl);
}
}
前臺發送的請求只需要帶上實現類的類名以及對應實現類需要的參數即可
tips
使用POI生成excel的方法在此暫不展示了,畢竟這個不屬于這個話題中研究的問題,如有需要poi使用的可以在評論中留言。
小結
很早之前就知道JDBC加載驅動的方式就是聲明接口,然后將實現類讓各個數據庫的廠商進行實現,因此在遇到這種情況的時候也可以想起使用這種模式,聲明一個接口,交給對應的實現類去實現,最后由統一方法進行數據整合輸出即可!