# Import Excel Automapping Entity (POI lib) ###### tags: `spring` 原本打算用poi-object-mapper處理上傳Excel,但瘋狂報錯,且要用的功能也很陽春,自己改成要的樣子 Step1. 建立@Annotation給Entity與上傳文件標頭mapping用 ```java= @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.METHOD}) public @interface ExcelColumn { String value() default ""; boolean ignore() default false; // 匯入時跳過此欄位 } ``` 該範例為使用JPA Support建立出來,且有使用lombok這個lib 有欄位名稱不同的添加@ExcelColumn ```java= @Data @Entity @Table(name = "CcyPairFXRate") @IdClass(CcyPairFXRateId.class) public class CcyPairFXRate implements Serializable { @Id @Column(name = "DataDate", nullable = false) @ExcelColumn("日期") private String dataDate; @Id @Column(name = "RateType", nullable = false) @ExcelColumn("類別") private String rateType; @Id @Column(name = "CCYPair", nullable = false) private String CCYPair; @Column(name = "Rate") @ExcelColumn("Rate") private Float rate; } ``` Step2. 初始化上傳文件 & 取得Excel標頭名稱的List ```java= XSSFWorkbook workbook = new XSSFWorkbook(multipartFile.getInputStream()); XSSFSheet worksheet = workbook.getSheetAt(0); XSSFRow row0 = worksheet.getRow(0); int num = row0.getLastCellNum() == 0 ? 0 : row0.getLastCellNum() - 1; List<String> headerArr = new ArrayList<>(); for (int i = 0; i <= num; i++) { headerArr.add(row0.getCell(i).getStringCellValue()); } ``` Step3. 取得Entity的欄位名稱與宣告的@ExcelColumn做mapping ```java= public T setEntityFromXSSFRow (T item, XSSFRow row, List<String> headerArr) throws NoSuchFieldException, IllegalAccessException { int colIndex = headerArr.size(); HashMap<String, String> modelMap = new HashMap<>(); for (Field f : item.getClass().getDeclaredFields()) { // 僅接受有輸入Annotation的欄位 // if (!f.isAnnotationPresent(SheetColumn.class)) { // continue; // } ExcelColumn ec = f.getAnnotation(ExcelColumn.class); if(ec != null && ec.ignore()) continue; // 跳過ignore String header = ec == null || ec.value() == null || ec.value().isEmpty() ? f.getName() : ec.value(); modelMap.put(header, f.getName()); } // Step4 // ... // Step4 } ``` Step4. 將取得的欄位標頭與內容轉值後放入Entity內 ```java= public T setEntityFromXSSFRow (T item, XSSFRow row, List<String> headerArr) throws NoSuchFieldException, IllegalAccessException { // step3 // ... // step3 int colIndex = headerArr.size(); for (int i = 0; i < colIndex; i++) { String fileName = modelMap.get(headerArr.get(i)); if(fileName == null || fileName.isEmpty()) { continue; } Field field = item.getClass().getDeclaredField(fileName); field.setAccessible(true); switch (row.getCell(i).getCellType().toString()) { case "STRING" -> field.set(item, row.getCell(i).getStringCellValue()); case "NUMERIC" -> { if (Float.class.equals(field.getType())) { field.set(item, (float) row.getCell(i).getNumericCellValue()); } else if (Integer.class.equals(field.getType())) { field.set(item, (int) row.getCell(i).getNumericCellValue()); } else { field.set(item, row.getCell(i).getNumericCellValue()); } } case "DATE" -> field.set(item, row.getCell(i).getDateCellValue()); } } return item; } ``` Step5. service 呼叫 mapper 將逐筆entity放回List中 ```java= List<CcyPairFXRate> ccy = new ArrayList<>(); Mapper<CcyPairFXRate> mapper = new Mapper<>(); for (int index = 1; index < worksheet.getPhysicalNumberOfRows(); index++) { CcyPairFXRate cc = new CcyPairFXRate(); XSSFRow row = worksheet.getRow(index); ccy.add(mapper.setEntityFromXSSFRow(cc, row, headerArr)); } ``` ref [poi-object-mapper](https://github.com/millij/poi-object-mapper)