基于Poi的注解导入Excel的方式
最近在做一个excel导入功能,项目中用的excel插件是easypoi。 其实easypoi已经可以实现基于注解的导入方式了, 但是在实际调用的时候总是报一个ClassNotFound异常。上网一查有人说是因为jar包版本不匹配导致的。 项目也是一个挺老的项目了, 也不敢贸然升级插件的版本,怕出一些问题。 没有办法,只能自己实现了。
其实这个工具实现的思路还是挺简单的: 首先先新建一个注解类, 然后在业务代码调用导入工具方法的时候去反射读取传入的class对象, 判断这个class对象的字段是否是新加的那个注解,如果是自定义注解就去建立字段与excel列索引的关联关系, 然后凭借着这个关联关系去循环地反射调用通过反射创建的object对象,最后把object加入到List并返回。 这样就可以实现excel转List了。
另外, 这只是一个临时创建的工具类, 只能实现最基本的数据读取。对于数据的校验,格式的验证,内存控制等一些高级功能远远不如easypoi或者easyexcel,如果有条件的话还是不要用我这个
--------------------------------------------------------------------------------------------------------------------------
下面是具体实现
自定义的@Import注解
package com.example.demo.utils; import java.lang.annotation.*; /** * 可以在导入实体的字段中加入此注解, * 调用ImportExcelUtil.excelToList(XSSFWorkbook workbook, Class<?> pojoClass) 将excel文件转化成List<T>集合 * @author lrxsz */ @Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Import { /** * 导入列首行的名称, 也就是表头 * @return */ String name(); /** * 是否是必填项, 如果此属性设置了true,但是excel中的单元格中的某行没有这列数据,则此行不会被读取到list中 * @return */ boolean must() default false; }
字段与excel列索引的映射关系类
package com.example.demo.utils; import lombok.Data; /** * 导入excel工具的exlcel与实体类的映射类 * @author lrxsz */ @Data public class ImportMap { /** * 注解的名称 */ String annotationName; /** * 字段的名称 */ String fieldName; /** * 字段的类型 */ Class<?> fieldTypeClass; /** * 所在行的列数(index) */ Integer cellNum; /** * 是否是必填项, 如果此属性设置了true,但是excel中没有此列数据,则此行不会被读取到list */ Boolean isMust; }
导入主工具类
package com.example.demo.utils; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.NumberToTextConverter; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; /** * 基于Poi的Excel导入工具类 * @author lrxsz */ public class ImportExcelUtil { /** * 时间转化类型 */ private static final String[] DATEPATTERN = { "yyyy-MM-dd","yyyy/MM/dd","yyyyMMdd","yyyy年MM月dd日", "yyyy-MM-dd HH:mm:ss","yyyy/MM/dd HH:mm:ss","yyyyMMdd HH:mm:ss","yyyy年MM月dd日 HH:mm:ss" }; private static final SimpleDateFormat format = new SimpleDateFormat(); /** * 获取表头与excel导入实体类字段的对应关系 * @return */ private static HashMap<Integer, ImportMap> getExcelMap(XSSFWorkbook workbook, Class<?> pojoClass) throws IOException { HashMap<Integer, ImportMap> excelMap = new HashMap<Integer, ImportMap>(); XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个 XSSFRow header = sheet.getRow(0); //获取表头 Field[] declaredFields = pojoClass.getDeclaredFields(); for(int i = 0; declaredFields != null && i < declaredFields.length; i ++) { if(declaredFields[i] == null) continue; Import annotation = declaredFields[i].getAnnotation(Import.class); if(annotation == null) continue; String fieldName = declaredFields[i].getName(); Class<?> type = declaredFields[i].getType(); String annotationName = annotation.name(); boolean must = annotation.must(); for(int j = 0; header != null && j < header.getLastCellNum(); j ++) { XSSFCell cell = header.getCell(j); String headerCell = getCellValue(cell); if(annotationName != null && annotationName.equals(headerCell)) { ImportMap map = new ImportMap(); map.setCellNum(j); map.setAnnotationName(annotationName); map.setFieldName(fieldName); map.setFieldTypeClass(type); map.setIsMust(must); excelMap.put(i, map); } } } return excelMap; } private static HashMap<Integer, ImportMap> getExcelMap(HSSFWorkbook workbook, Class<?> pojoClass) throws IOException { HashMap<Integer, ImportMap> excelMap = new HashMap<Integer, ImportMap>(); //XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个 HSSFSheet sheet = workbook.getSheetAt(0); //XSSFRow header = sheet.getRow(0); //获取表头 HSSFRow header = sheet.getRow(0); Field[] declaredFields = pojoClass.getDeclaredFields(); for(int i = 0; declaredFields != null && i < declaredFields.length; i ++) { if(declaredFields[i] == null) continue; Import annotation = declaredFields[i].getAnnotation(Import.class); if(annotation == null) continue; String fieldName = declaredFields[i].getName(); Class<?> type = declaredFields[i].getType(); String annotationName = annotation.name(); boolean must = annotation.must(); for(int j = 0; header != null && j < header.getLastCellNum(); j ++) { //XSSFCell cell = header.getCell(j); HSSFCell cell = header.getCell(j); String headerCell = getCellValue(cell); if(annotationName != null && annotationName.equals(headerCell)) { ImportMap map = new ImportMap(); map.setCellNum(j); map.setAnnotationName(annotationName); map.setFieldName(fieldName); map.setFieldTypeClass(type); map.setIsMust(must); excelMap.put(i, map); } } } return excelMap; } /** * 获取最后一个必填项所在列的列数(index) * @param workbook * @param excelMap * @return */ static int getLastMustIndex(XSSFWorkbook workbook, HashMap<Integer, ImportMap> excelMap) { XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个 XSSFRow header = sheet.getRow(0); //获取表头 int result = 0; for(int i = 0; header != null && i < header.getLastCellNum(); i ++) { ImportMap importMap = excelMap.get(i); if(importMap != null && importMap.getIsMust()) { result = i; } } return result; } static int getLastMustIndex(HSSFWorkbook workbook, HashMap<Integer, ImportMap> excelMap) { //XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个 HSSFSheet sheet = workbook.getSheetAt(0); //XSSFRow header = sheet.getRow(0); //获取表头 HSSFRow header = sheet.getRow(0); int result = 0; for(int i = 0; header != null && i < header.getLastCellNum(); i ++) { ImportMap importMap = excelMap.get(i); if(importMap != null && importMap.getIsMust()) { result = i; } } return result; } public static <T> List<T> excelToList(HSSFWorkbook workbook, Class<?> pojoClass) throws Exception { HashMap<Integer, ImportMap> excelMap = getExcelMap(workbook, pojoClass); int lastMustIndex = getLastMustIndex(workbook, excelMap);//最后一个必填项的index //XSSFSheet sheet = workbook.getSheetAt(0); //默认导入第一个 HSSFSheet sheet = workbook.getSheetAt(0); List list = new ArrayList(); for (int rowNum = 1; rowNum < sheet.getLastRowNum() + 1 && null != sheet; rowNum++) { //XSSFRow row = sheet.getRow(rowNum); HSSFRow row = sheet.getRow(rowNum); short lastCellNum = row.getLastCellNum(); if(row != null && lastCellNum < (lastMustIndex + 1)) { continue; } Object object = pojoClass.newInstance(); for(int i = 0; row != null && i < lastCellNum; i ++) { //XSSFCell cell = row.getCell(i); HSSFCell cell = row.getCell(i); String cellContent = getCellValue(cell); ImportMap importMap = excelMap.get(i); if(importMap == null) { break; } if(importMap.getIsMust() && isEmpty(cellContent)) { object = null; break; } String methodName = "set" + capitalizeFirstLetter(importMap.getFieldName()); try { Method method = pojoClass.getMethod(methodName, importMap.getFieldTypeClass()); if(cellContent == null || cellContent.length() == 0) continue; method.invoke(object, convertType(importMap.getFieldTypeClass(), cellContent)); } catch (Exception e) { //e.printStackTrace(); throw new Exception("\"" + importMap.getAnnotationName() + "\"数据格式不正确"); } } if(object != null) { list.add(object); } } return list; } /** * Excel转List<T> * 此方法仅支持xlsx * 需要在导入实体类的字段中加入@Import注解 * @param workbook * @param pojoClass * @param <T> * @return * @throws IllegalAccessException * @throws InstantiationException * @throws IOException * @throws NoSuchFieldException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws ParseException */ public static <T> List<T> excelToList(XSSFWorkbook workbook, Class<?> pojoClass) throws Exception { HashMap<Integer, ImportMap> excelMap = getExcelMap(workbook, pojoClass); int lastMustIndex = getLastMustIndex(workbook, excelMap);//最后一个必填项的index XSSFSheet sheet = workbook.getSheetAt(0); //默认导入第一个 List list = new ArrayList(); for (int rowNum = 1; rowNum < sheet.getLastRowNum() + 1 && null != sheet; rowNum++) { XSSFRow row = sheet.getRow(rowNum); short lastCellNum = row.getLastCellNum(); if(row != null && lastCellNum < (lastMustIndex + 1)) { continue; } Object object = pojoClass.newInstance(); for(int i = 0; row != null && i < lastCellNum; i ++) { XSSFCell cell = row.getCell(i); String cellContent = getCellValue(cell); ImportMap importMap = excelMap.get(i); if(importMap == null) { break; } if(importMap.getIsMust() && isEmpty(cellContent)) { object = null; break; } String methodName = "set" + capitalizeFirstLetter(importMap.getFieldName()); try { Method method = pojoClass.getMethod(methodName, importMap.getFieldTypeClass()); if(cellContent == null || cellContent.length() == 0) continue; method.invoke(object, convertType(importMap.getFieldTypeClass(), cellContent)); } catch (Exception e) { //e.printStackTrace(); throw new Exception("\"" + importMap.getAnnotationName() + "\"数据格式不正确"); } } if(object != null) { list.add(object); } } return list; } private static boolean isEmpty(String cellContent) { if(cellContent == null) { return true; } if(cellContent.length() == 0) { return true; } return false; } private static Object convertType(Class<?> fieldTypeClass, String cellContent) throws Exception { String name = fieldTypeClass.getName(); if(fieldTypeClass == String.class) { return cellContent.trim(); } if(fieldTypeClass == Integer.class || "int".equals(name)) { if(isEmpty(cellContent)) { return 0; } else { return Integer.parseInt(cellContent); } } if(fieldTypeClass == Short.class || "short".equals(name)) { if(isEmpty(cellContent)) { new Short("0"); } else { return Short.parseShort(cellContent); } } if(fieldTypeClass == Long.class || "long".equals(name)) { if(isEmpty(cellContent)) { return 0L; } else { return Long.parseLong(cellContent); } } if(fieldTypeClass == Double.class || "double".equals(name)) { if(isEmpty(cellContent)) { return 0D; } else { return Double.parseDouble(cellContent); } } if(fieldTypeClass == Float.class || "float".equals(name)) { if(isEmpty(cellContent)) { return 0F; } else { return Float.parseFloat(cellContent); } } if(fieldTypeClass == Byte.class || "byte".equals(name)) { if(isEmpty(cellContent)) { return new Byte("0"); } else { return Byte.parseByte(cellContent); } } if(fieldTypeClass == Character.class || "char".equals(name)) { if(isEmpty(cellContent)) { char result = 0; return result; } else { return cellContent.charAt(0); } } if(fieldTypeClass == Boolean.class || "boolean".equals(name)) { if(isEmpty(cellContent)) { return false; } else { return Boolean.parseBoolean(cellContent); } } if(isEmpty(cellContent)) { return null; } if(fieldTypeClass == BigDecimal.class) { return new BigDecimal(cellContent); } if(fieldTypeClass == Date.class) { int i1 = 0; for(int i = 0; DATEPATTERN != null && i < DATEPATTERN.length; i ++) { format.applyPattern(DATEPATTERN[i]); try { Date parse = format.parse(cellContent); return parse; } catch (Exception e) { i1++; } } if(DATEPATTERN != null && i1 == DATEPATTERN.length) { throw new Exception("日期不合法"); } } return null; } private static String capitalizeFirstLetter(String original) { if (original == null || original.length() == 0) { return original; } return original.substring(0, 1).toUpperCase() + original.substring(1); } /** * Cell 转换为String * @param cell * @return String */ private static String getCellValue(Cell cell) { String ret; if(null != cell){ switch (cell.getCellType().getCode()) { case 3: ret = ""; break; case 4: ret = String.valueOf(cell.getBooleanCellValue()); break; case 5: ret = ""; break; case 2: Workbook wb = cell.getSheet().getWorkbook(); CreationHelper crateHelper = wb.getCreationHelper(); FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator(); ret = getCellValue(evaluator.evaluateInCell(cell)); break; case 0: if (DateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式 SimpleDateFormat sdf = null; if (cell.getCellStyle().getDataFormat() == HSSFDataFormat .getBuiltinFormat("h:mm")) { sdf = new SimpleDateFormat("HH:mm"); } else {// 日期 sdf = new SimpleDateFormat("yyyy-MM-dd"); } Date date = cell.getDateCellValue(); ret = sdf.format(date); } else if (cell.getCellStyle().getDataFormat() == 58) { // 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58) SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); double value = cell.getNumericCellValue(); Date date = org.apache.poi.ss.usermodel.DateUtil .getJavaDate(value); ret = sdf.format(date); } else { ret = NumberToTextConverter.toText(cell.getNumericCellValue()); // double value = cell.getNumericCellValue(); // CellStyle style = cell.getCellStyle(); // DecimalFormat format = new DecimalFormat(); // String temp = style.getDataFormatString(); // // 单元格设置成常规 // if (temp.equals("General")) { // format.applyPattern("#"); // } // ret = format.format(value); } break; case 1: ret = cell.getRichStringCellValue().getString(); break; default: ret = ""; } }else{ ret = ""; } return ret.trim(); } }
然后是测试, 先新建一个测试excel
导入实体:
package com.example.demo.structs; import com.example.demo.utils.Import; import lombok.Data; import java.math.BigDecimal; import java.util.Date; @Data public class PoiImportAnnotationTest { @Import(name="测试列1") String testColumn1; @Import(name="测试列2") String testColumn2; @Import(name="测试列3") String testColumn3; @Import(name="测试列4") String testColumn4; @Import(name="测试列5") String testColumn5; @Import(name="number字段1") Integer number1; @Import(name="number字段2") Integer number2; @Import(name="date日期1") Date date1; @Import(name="date日期2") Date date2; @Import(name="bigdecimal1") BigDecimal bigDecimal1; @Import(name="bigdecimal2") BigDecimal bigDecimal2; }
运行:
工具中有两个excelToList方法, 其中 XSSFWorkbook是07版本的xlsx, HSSFWorkbook是03版本的xls。