# 筆記: JAVA反射應用_EXCEL輸出 工作上需要將資料庫中的資料輸出為EXCEL的樣式 常見的是使用apache poi 套件中的workbook去帶入資料庫資料 在帶入時會用for迴圈將需要的資料填入每一個資料格(cell) 如不用在填入資料時至少要依照欄位的多寡寫出相對數量的code,若有很多欄位這段會寫得很長且日後要修改不方便 *由於重點在反射, 這邊會省略Apache poi的引入方式和簡述該套件應用上的說明 *下面的範例也能抽出寫成共用方法,如有excel固定需求可以一併寫入 程式上會建立org.apache.poi.ss.usermodel.**Workbook**的實作類別 有兩種格式**HSSF**和**XSSF**,分別對應副檔名xls和xlsx 這邊使用XSSFWorkbook Sheet: 工作表 Row: 行 Cell:格 普遍格式如下, 第一行為標題行之後均為資料行 有其他不同的形式請再根據實際情況做變化 | 欄1| 欄2 | 欄3 | | -------- | -------- | -------- | | Cell | Cell | Cell | | Cell | Cell | Cell | 呈現結果類似下圖,單純調過欄寬, 其他一些格式調整可以在程式裡控制 ![image](https://hackmd.io/_uploads/rkkrodeo6.png) --- ## 帶入資料庫資料的前置工作 ``` // ...前略資料查詢的部分程式 Workbook workbook = new XSSFWorkbook(); // 建立工作表 Sheet sheet = workbook.createSheet("訂貨清單"); // 設置所需的excel格式設定 sheet.setDefaultColumnWidth(30); ``` 接著需要準備一個定義標題欄的資料當你需要在標頭顯示欄位名稱, 這邊個人使用LinkedHashMap做為設定excel header的容器 key對應資料物件的屬性名;value設定為欄位名稱 也可以使用其他方式作定義, 如用物件或從DB提取相關的設定, 端看設計方式和需求 ``` Map<String, String> headerTitleMap = new LinkedHashMap<String, String>(); headerTitleMap.put("field1", "產品類別"); headerTitleMap.put("field2", "業務員編號/姓名"); headerTitleMap.put("field3", "客戶名稱"); headerTitleMap.put("field4", "訂貨時間"); headerTitleMap.put("field5", "狀態"); ``` 進入填充excel資料的部分 這邊第一層迴圈使用的queryList為查詢的資料,因為每筆資料都相當於每一行 但注意第一行為(i=0)標題行, 資料行的處理從(i=1)開始 *做excel處理基本上就要用到兩層迴圈了(除非固定工作表),再加上使用反射去處理cell塞資料會有一些clean code上的問題建議要再處理過 大概會如下的結構 ``` for(int i==0;資料集合 >=i;i++){ for(Entry<String, String> title: headerTitleMap.entrySet()){ } } ``` --- 現在我們專注在有使用到反射的部分 我們將儲存資料的物件dataObj 使用Class的反射方法getDeclaredFields()去得到Field[]; ``` Field[] fields = dataObj.getClass().getDeclaredFields(); ``` 這個Field陣列內會有當下這個dataObj物件各個屬性的相關資料,之後將這個Field[]的陣列用迴圈取出Filed物件 ![image](https://hackmd.io/_uploads/S1drQsxsT.png) 其中我們會用到name這個屬性,它會記錄著該物件的屬性名稱 因此我們在和headerTitleMap(先前設定的標題資料)內的key做比對就可以map到目前的cell要放入的資料了 ``` if(key.equals(field.getName())) { // 屬性名和titile欄位符合時 if(key.equals(field.getName())) { field.setAccessible(true);// 要設定要能夠讀取物件內private的屬性 // null處理 String value = String.valueOf(field.get(dataObj)==null?"":field.get(dataObj)); cell.setCellValue(String.valueOf(value)); break;//塞值後跳出換下一個欄位 } } ``` 整段處理資料的程式碼 ``` for(int i=0; queryList.size() >= i ;i++) {// 行 Row row = sheet.createRow(i); if(i == 0) {//excel title setValue int j = 0; for(Entry<String, String> title: headerTitleMap.entrySet()) {// 欄 Cell cell = row.createCell(j); cell.setCellValue(title.getValue()); j++; } } if(i > 0) {// data row IBPMF070100PageResVo dataObj = queryList.get(i-1); //null處理 String renewtimeStr = dataObj.getRenewTime()==null? "": sdf.format(dataObj.getRenewTime()); dataObj.setRenewTimeStr(renewtimeStr); Field[] fields = dataObj.getClass().getDeclaredFields(); int j =0; for(Entry<String, String> title: headerTitleMap.entrySet()) { // 欄 String key = title.getKey(); Cell cell = row.createCell(j); for(Field field: fields) { // 屬性名和titile欄位符合時 if(key.equals(field.getName())) { field.setAccessible(true); // null處理 String value = String.valueOf(field.get(dataObj)==null?"":field.get(dataObj)); cell.setCellValue(String.valueOf(value)); break;//塞值後跳出換下一個欄位 } } j++; } } } ```