Excel基於POI導入導出的Annotation化之路(一)

Excel在web項目裏的使用變得愈來愈普遍,特別是和線下耦合度較高的業務,Excel導入導出變得很是頻繁,儘管不少人寫了諸多的工具方法,可是終究沒有解決一個問題:有效的控制字段英文名稱和實際表頭名稱(這裏指Excel中文表頭名稱)的對應關係,在編碼開發過程當中,大量時間用於解決這些問題,並所以衍生出大量的工做量,以致於硬性的加了許多約定,在開發過程當中,工具方法通用性並不高。所以,基於實體Bean的Annotation化是一個比較不錯的嘗試。java

Excel導入到Beanweb

public class ExcelToBean{
    
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private int etimes = 0;
    
    
    /**
     * 從文件讀取數據,最好是全部的單元格都是文本格式,日期格式要求yyyy-MM-dd HH:mm:ss,布爾類型0:真,1:假
     * 
     * @param edf
     *            數據格式化
     * 
     * @param file
     *            Excel文件,支持xlsx後綴,xls的沒寫,基本同樣
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public <E> List<E> readFromFile(ExcelDataFormatter edf, File file, Class<?> clazz) throws Exception {
        Field[] fields = ReflectUtils.getClassFieldsAndSuperClassFields(clazz);

        Map<String, String> textToKey = new HashMap<String, String>();

        Excel _excel = null;
        for (Field field : fields) {
            _excel = field.getAnnotation(Excel.class);
            if (_excel == null || _excel.skip() == true) {
                continue;
            }
            textToKey.put(_excel.name(), field.getName());
        }

        InputStream is = new FileInputStream(file);

        Workbook wb = new XSSFWorkbook(is);

        Sheet sheet = wb.getSheetAt(0);
        Row title = sheet.getRow(0);
        // 標題數組,後面用到,根據索引去標題名稱,經過標題名稱去字段名稱用到 textToKey
        String[] titles = new String[title.getPhysicalNumberOfCells()];
        for (int i = 0; i < title.getPhysicalNumberOfCells(); i++) {
            titles[i] = title.getCell(i).getStringCellValue();
        }

        List<E> list = new ArrayList<E>();

        E e = null;

        int rowIndex = 0;
        int columnCount = titles.length;
        Cell cell = null;
        Row row = null;

        for (Iterator<Row> it = sheet.rowIterator(); it.hasNext();) {

            row = it.next();
            if (rowIndex++ == 0) {
                continue;
            }

            if (row == null) {
                break;
            }

            e = (E)clazz.newInstance();

            for (int i = 0; i < columnCount; i++) {
                cell = row.getCell(i);
                if(null==cell)continue;
                etimes = 0;
                readCellContent(textToKey.get(titles[i]), fields, cell, e, edf);
            }
            list.add(e);
        }
        return list;
    }
    
    public static void main(String[] args) throws Exception {
    }

    /**
     * 從單元格讀取數據,根據不一樣的數據類型,使用不一樣的方式讀取<br>
     * 有時候常常和咱們期待的數據格式不同,會報異常,<br>
     * 咱們這裏採起強硬的方式<br>
     * 使用各類方法,知道嘗試到讀到數據爲止,而後根據Bean的數據類型,進行相應的轉換<br>
     * 若是嘗試完了(總共7次),仍是不能獲得數據,那麼拋個異常出來,沒辦法了
     * 
     * @param key
     *            當前單元格對應的Bean字段
     * @param fields
     *            Bean全部的字段數組
     * @param cell
     *            單元格對象
     * @param obj
     * @throws Exception
     */
    public void readCellContent(String key, Field[] fields, Cell cell, Object obj, ExcelDataFormatter edf) throws Exception {

        Object o = null;
        
        try {
            switch (cell.getCellType()) {
            case XSSFCell.CELL_TYPE_BOOLEAN:
                o = cell.getBooleanCellValue();
                break;
            case XSSFCell.CELL_TYPE_NUMERIC:
                o = cell.getNumericCellValue();
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    o = DateUtil.getJavaDate(cell.getNumericCellValue());
                }
                break;
            case XSSFCell.CELL_TYPE_STRING:
                o = cell.getStringCellValue();
                break;
            case XSSFCell.CELL_TYPE_ERROR:
                o = cell.getErrorCellValue();
                break;
            case XSSFCell.CELL_TYPE_BLANK:
                o = null;
                break;
            case XSSFCell.CELL_TYPE_FORMULA:
                o = cell.getCellFormula();
                break;
            default:
                o = null;
                break;
            }

            if (o == null)
                return;

            for (Field field : fields) {
                field.setAccessible(true);
                if (field.getName().equals(key)) {
                    Boolean bool = true;
                    Map<String, String> map = null;
                    if (edf == null) {
                        bool = false;
                    } else {
                        map = edf.get(field.getName());
                        if (map == null) {
                            bool = false;
                        }
                    }

                    if (field.getType().equals(Date.class)) {
                        if (o.getClass().equals(Date.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, sdf.parse(o.toString()));
                        }
                    } else if (field.getType().equals(String.class)) {
                        if (o.getClass().equals(String.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, o.toString());
                        }
                    } else if (field.getType().equals(Long.class)) {
                        if (o.getClass().equals(Long.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, Long.parseLong(o.toString()));
                        }
                    } else if (field.getType().equals(Integer.class)) {
                        if (o.getClass().equals(Integer.class)) {
                            field.set(obj, o);
                        } else {
                            // 檢查是否須要轉換
                            String ostr = o.toString();
                            ostr = ostr.split("\\.").length>0?ostr.split("\\.")[0]:ostr;
                            if (bool) {
                                field.set(obj, map.get(ostr) != null ? Integer.parseInt(map.get(ostr)) : Integer.parseInt(ostr));
                            } else {
                                field.set(obj, Integer.parseInt(ostr));
                            }

                        }
                    } else if (field.getType().equals(BigDecimal.class)) {
                        if (o.getClass().equals(BigDecimal.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, BigDecimal.valueOf(Double.parseDouble(o.toString())));
                        }
                    } else if (field.getType().equals(Boolean.class)) {
                        if (o.getClass().equals(Boolean.class)) {
                            field.set(obj, o);
                        } else {
                            // 檢查是否須要轉換
                            if (bool) {
                                field.set(obj, map.get(o.toString()) != null ? Boolean.parseBoolean(map.get(o.toString())) : Boolean.parseBoolean(o.toString()));
                            } else {
                                field.set(obj, Boolean.parseBoolean(o.toString()));
                            }
                        }
                    } else if (field.getType().equals(Float.class)) {
                        if (o.getClass().equals(Float.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, Float.parseFloat(o.toString()));
                        }
                    } else if (field.getType().equals(Double.class)) {
                        if (o.getClass().equals(Double.class)) {
                            field.set(obj, o);
                        } else {
                            field.set(obj, Double.parseDouble(o.toString()));
                        }

                    }

                }
            }

        } catch (Exception ex) {
            ex.printStackTrace();
            // 若是仍是讀到的數據格式仍是不對,只能放棄了
            if (etimes > 7) {
                throw ex;
            }
            etimes++;
            if (o == null) {
                readCellContent(key, fields, cell, obj, edf);
            }
        }
    }

}

Bean導出到Excel數據庫

public class BeanToExcel {
    /**
     * 得到Workbook對象
     * 
     * @param list
     *            數據集合
     * @return Workbook
     * @throws Exception
     */
    public static <T> Workbook getWorkBook(List<T> list, ExcelDataFormatter edf) throws Exception {
        // 建立工做簿
        Workbook wb = new SXSSFWorkbook();

        if (list == null || list.size() == 0)
            return wb;

        // 建立一個工做表sheet
        Sheet sheet = wb.createSheet();
        // 申明行
        Row row = sheet.createRow(0);
        // 申明單元格
        Cell cell = null;

        CreationHelper createHelper = wb.getCreationHelper();

        Field[] fields = ReflectUtils.getClassFieldsAndSuperClassFields(list.get(0).getClass());

        XSSFCellStyle titleStyle = (XSSFCellStyle) wb.createCellStyle();
        titleStyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
        // 設置前景色
        titleStyle.setFillForegroundColor(new XSSFColor(new java.awt.Color(159, 213, 183)));
        titleStyle.setAlignment(CellStyle.ALIGN_CENTER);

        Font font = wb.createFont();
        font.setColor(HSSFColor.BROWN.index);
        font.setBoldweight(Font.BOLDWEIGHT_BOLD);
        // 設置字體
        titleStyle.setFont(font);

        int columnIndex = 0;
        Excel excel = null;
        for (Field field : fields) {
            field.setAccessible(true);
            excel = field.getAnnotation(Excel.class);
            if (excel == null || excel.skip() == true) {
                continue;
            }
            // 列寬注意乘256
            sheet.setColumnWidth(columnIndex, excel.width() * 256);
            // 寫入標題
            cell = row.createCell(columnIndex);
            cell.setCellStyle(titleStyle);
            cell.setCellValue(excel.name());

            columnIndex++;
        }

        int rowIndex = 1;

        CellStyle cs = wb.createCellStyle();

        for (T t : list) {
            row = sheet.createRow(rowIndex);
            columnIndex = 0;
            Object o = null;
            for (Field field : fields) {

                field.setAccessible(true);

                // 忽略標記skip的字段
                excel = field.getAnnotation(Excel.class);
                if (excel == null || excel.skip() == true) {
                    continue;
                }
                // 數據
                cell = row.createCell(columnIndex);

                o = field.get(t);
                // 若是數據爲空,跳過
                if (o == null)
                    continue;

                // 處理日期類型
                if (o instanceof Date) {
                    // excel.dateFormat()獲取註解的日期格式,默認yyyy-MM-dd HH:mm:ss
                    cs.setDataFormat(createHelper.createDataFormat().getFormat(excel.dateFormat()));
                    cell.setCellStyle(cs);
                    cell.setCellValue((Date) field.get(t));
                } else if (o instanceof Double || o instanceof Float) {// 浮點數
                    cell.setCellValue(field.get(t).toString());
                    if (excel.precision() != -1) {
                        cell.setCellValue(new BigDecimal(field.get(t).toString()).setScale(excel.precision(), excel.round() == true ? BigDecimal.ROUND_HALF_UP : BigDecimal.ROUND_FLOOR).toString());
                    }
                } else if (o instanceof BigDecimal) {// BigDecimal
                    cell.setCellValue((field.get(t).toString()));
                    if (excel.precision() != -1) {
                        cell.setCellValue(new BigDecimal(field.get(t).toString()).setScale(excel.precision(), excel.round() == true ? BigDecimal.ROUND_HALF_UP : BigDecimal.ROUND_FLOOR).toString());
                    }
                } else if (o instanceof Boolean) {// 布爾類型
                    Boolean bool = (Boolean) field.get(t);
                    if (edf == null) {
                        cell.setCellValue(bool);
                    } else {
                        Map<String, String> map = edf.get(field.getName());
                        if (map == null) {
                            cell.setCellValue(bool);
                        } else {
                            cell.setCellValue(map.get(bool.toString().toLowerCase()));
                        }
                    }

                } else if (o instanceof Integer) {// 整型

                    Integer intValue = (Integer) field.get(t);

                    if (edf == null) {
                        cell.setCellValue(intValue);
                    } else {
                        Map<String, String> map = edf.get(field.getName());
                        if (map == null) {
                            cell.setCellValue(intValue);
                        } else {
                            cell.setCellValue(map.get(intValue.toString()));
                        }
                    }
                } else {
                    cell.setCellValue(field.get(t).toString());
                }

                columnIndex++;
            }

            rowIndex++;
        }

        return wb;
    }

    /**
     * 將數據寫入到EXCEL文檔
     * 
     * @param list
     *            數據集合
     * @param edf
     *            數據格式化,好比有些數字表明的狀態,像是0:女,1:男,或者0:正常,1:鎖定,變成可讀的文字
     *            該字段僅僅針對Boolean,Integer兩種類型做處理
     * @param filePath
     *            文件路徑
     * @throws Exception
     */
    public static <T> void writeToFile(List<T> list, ExcelDataFormatter edf, String filePath) throws Exception {
        // 建立並獲取工做簿對象
        Workbook wb = getWorkBook(list, edf);
        // 寫入到文件
        FileOutputStream out = new FileOutputStream(filePath);
        wb.write(out);
        out.close();
    }

數據格式化工具類數組

public class ExcelDataFormatter {
     
    private Map<String,Map<String,String>> formatter=new HashMap<String, Map<String,String>>();
 
    public void set(String key,Map<String,String> map){
        formatter.put(key, map);
    }
     
    public Map<String,String> get(String key){
        return formatter.get(key);
    }
     
}

反射工具類工具

public class ReflectUtils {

    /**
     * 獲取成員變量的修飾符
     * 
     * @param clazz
     * @param field
     * @return
     * @throws Exception
     */
    public static <T> int getFieldModifier(Class<T> clazz, String field) throws Exception {
        // getDeclaredFields能夠獲取全部修飾符的成員變量,包括private,protected等getFields則不能夠
        Field[] fields = clazz.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(field)) {
                return fields[i].getModifiers();
            }
        }
        throw new Exception(clazz + " has no field \"" + field + "\"");
    }

    /**
     * 獲取成員方法的修飾符
     * 
     * @param clazz
     * @param method
     * @return
     * @throws Exception
     */
    public static <T> int getMethodModifier(Class<T> clazz, String method) throws Exception {

        // getDeclaredMethods能夠獲取全部修飾符的成員方法,包括private,protected等getMethods則不能夠
        Method[] m = clazz.getDeclaredMethods();

        for (int i = 0; i < m.length; i++) {
            if (m[i].getName().equals(m)) {
                return m[i].getModifiers();
            }
        }
        throw new Exception(clazz + " has no method \"" + m + "\"");
    }

    /**
     * [對象]根據成員變量名稱獲取其值
     * 
     * @param clazzInstance
     * @param field
     * @return
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    public static <T> Object getFieldValue(Object clazzInstance, Object field) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        Field[] fields = clazzInstance.getClass().getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(field)) {
                // 對於私有變量的訪問權限,在這裏設置,這樣便可訪問Private修飾的變量
                fields[i].setAccessible(true);
                return fields[i].get(clazzInstance);
            }
        }

        return null;
    }

    /**
     * [類]根據成員變量名稱獲取其值(默認值)
     * 
     * @param clazz
     * @param field
     * @return
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> Object getFieldValue(Class<T> clazz, String field) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {

        Field[] fields = clazz.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName().equals(field)) {
                // 對於私有變量的訪問權限,在這裏設置,這樣便可訪問Private修飾的變量
                fields[i].setAccessible(true);
                return fields[i].get(clazz.newInstance());
            }
        }

        return null;
    }

    /**
     * 獲取全部的成員變量(經過GET,SET方法獲取)
     * 
     * @param clazz
     * @return
     */
    public static <T> String[] getFields(Class<T> clazz) {

        Field[] fields = clazz.getDeclaredFields();

        String[] fieldsArray = new String[fields.length];

        for (int i = 0; i < fields.length; i++) {
            fieldsArray[i] = fields[i].getName();
        }

        return fieldsArray;
    }

    /**
     * 獲取全部的成員變量,包括父類
     * 
     * @param clazz
     * @param superClass
     *            是否包括父類
     * @return
     * @throws Exception
     */
    public static <T> Field[] getFields(Class<T> clazz, boolean superClass) throws Exception {

        Field[] fields = clazz.getDeclaredFields();
        Field[] superFields = null;
        if (superClass) {
            Class superClazz = clazz.getSuperclass();
            if (superClazz != null) {
                superFields = superClazz.getDeclaredFields();
            }
        }

        Field[] allFields = null;

        if (superFields == null || superFields.length == 0) {
            allFields = fields;
        } else {
            allFields = new Field[fields.length + superFields.length];
            for (int i = 0; i < fields.length; i++) {
                allFields[i] = fields[i];
            }
            for (int i = 0; i < superFields.length; i++) {
                allFields[fields.length + i] = superFields[i];
            }
        }

        return allFields;
    }

    /**
     * 獲取全部的成員變量,包括父類
     * 
     * @param clazz
     * @return
     * @throws Exception
     */
    public static <T> Field[] getClassFieldsAndSuperClassFields(Class<T> clazz) throws Exception {

        Field[] fields = clazz.getDeclaredFields();

        if (clazz.getSuperclass() == null) {
            throw new Exception(clazz.getName() + "沒有父類");
        }

        Field[] superFields = clazz.getSuperclass().getDeclaredFields();

        Field[] allFields = new Field[fields.length + superFields.length];

        for (int i = 0; i < fields.length; i++) {
            allFields[i] = fields[i];
        }
        for (int i = 0; i < superFields.length; i++) {
            allFields[fields.length + i] = superFields[i];
        }

        return allFields;
    }

    /**
     * 指定類,調用指定的無參方法
     * 
     * @param clazz
     * @param method
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    public static <T> Object invoke(Class<T> clazz, String method) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
        Object instance = clazz.newInstance();
        Method m = clazz.getMethod(method, new Class[] {});
        return m.invoke(instance, new Object[] {});
    }

    /**
     * 經過對象,訪問其方法
     * 
     * @param clazzInstance
     * @param method
     * @return
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws InstantiationException
     */
    public static <T> Object invoke(Object clazzInstance, String method) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
        Method m = clazzInstance.getClass().getMethod(method, new Class[] {});
        return m.invoke(clazzInstance, new Object[] {});
    }

    /**
     * 指定類,調用指定的方法
     * 
     * @param clazz
     * @param method
     * @param paramClasses
     * @param params
     * @return Object
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public static <T> Object invoke(Class<T> clazz, String method, Class<T>[] paramClasses, Object[] params) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        Object instance = clazz.newInstance();
        Method _m = clazz.getMethod(method, paramClasses);
        return _m.invoke(instance, params);
    }

    /**
     * 經過類的實例,調用指定的方法
     * 
     * @param clazzInstance
     * @param method
     * @param paramClasses
     * @param params
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    public static <T> Object invoke(Object clazzInstance, String method, Class<T>[] paramClasses, Object[] params) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        Method _m = clazzInstance.getClass().getMethod(method, paramClasses);
        return _m.invoke(clazzInstance, params);
    }

//    @SuppressWarnings("unchecked")
//    public static void main(String[] args) throws Exception {
//        // getFields(User.class);
//        User u = new User();
//        invoke(u, "setName", new Class[] { String.class }, new Object[] { "xx發大水法大水法x" });
//        System.out.println(getFieldValue(u, "name"));
//    }

Annotation定義類字體

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface Excel {    
    
    //列名
    String name() default "";
    
    //寬度
    int width() default 20;

    //忽略該字段
    boolean skip() default false;
    
    //日期格式化
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";
    
    //浮點數的精度
    int precision() default -1;
    
    //四捨五入
    boolean round() default true;
    
}

使用方法編碼

以Excel導入作示例:spa

以下圖所示的Excel數據excel

在寫代碼前,按照Annotation的定義對實體Bean進行添加相關標籤,例以下圖:code

示例代碼:

public class ElearnSysExcelService extends CustomService{
    
    /**
     * 導入Excel文件數據到數據庫
     * @param file
     * @author zhanglongping
     * @date 2016-11-10 下午2:10:24
     */
    public static void importExcelToDB(File file){
        ExcelToBean e = new ExcelToBean();
        ExcelDataFormatter edf = new ExcelDataFormatter();
        Map<String,String> map = new HashMap<String,String>();
        map.put( "準備就緒","111");
        map.put( "開始","222");
        map.put( "工做","333");
        map.put( "結束","444");
        edf.set("state", map);

        try {
            List<ElearnSysUser> list =  e.readFromFile(edf, file, ElearnSysUser.class);
            for(ElearnSysUser esu:list){
                System.out.println(esu.getUserName()+"~~~"+esu.getState()+"~~~"+esu.getRealName());
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        ElearnSysExcelService e = new ElearnSysExcelService();
        File file = new File("f://test/excel_sys_user.xlsx");
        e.importExcelToDB(file);
    }
}

運行結果:

能夠看到,Excel中的狀態數據已被轉換爲111,444等數據

參考文獻:http://www.xdemo.org

相關文章
相關標籤/搜索