# 筆記: 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 |
呈現結果類似下圖,單純調過欄寬, 其他一些格式調整可以在程式裡控制

---
## 帶入資料庫資料的前置工作
```
// ...前略資料查詢的部分程式
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物件

其中我們會用到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++;
}
}
}
```