代码背景
1、人员信息导出为Excel的时候,简化操作步骤,直接通过解析数据对象Do的成员变量作用域来生成Excel的数据 2、人员信息Excel导入的时候,直接通过成员变量名逆向拼装获取Set方法,自动封装数据对象Do,以便保存到数据库中 3、以上操作的复用,针对不同类别的人员,相似的操作可以通过修改极少量的代码来实现功能
一、利用反射解析数据对象Do成员变量(PrisonMemberDo)
/** * 导出 * * @param memberQo * @return */ @Override public String export(PrisonMemberQo memberQo) throws BusinessException { final List<PrisonMemberDo> prisonMemberDoList = prisonMemberMapper.query(memberQo); final List<List<String>> exportList = getExportList(prisonMemberDoList); final String folderPath = propertyConfig.get("export.url", "/icooper/appdata/hbsf/export/"); final String fileName = ExportConstants.FILE_NAME_PRISON_MEM + ExportConstants.UNDER_LINE + TimeUtils.currTime1() + SCConstants.FMT_XLS; final String filePath = folderPath + fileName; final ExportExcel ex = new ExportExcel(ExportConstants.TITLE_PRISON_MEM, ExportConstants.HEADER_PRISON_MEM, exportList, folderPath, fileName); try { ex.export(); } catch (final Exception e) { log.error("服刑人员导出失败", e); throw new BusinessException("服刑人员导出失败"); } return filePath; } //省略部分代码... /** * 导出数据解析 * * @param dataList * @return */ private List<List<String>> getExportList(List<PrisonMemberDo> dataList) { final List<List<String>> exportList = new ArrayList<List<String>>(); int rowNo = 0; for (final PrisonMemberDo prisonMemberDo : dataList) { final List<String> rowData = new ArrayList<String>(); rowNo += 1; rowData.add(String.valueOf(rowNo)); autoFillRowData(rowData, prisonMemberDo, PrisonMemberDo.class); exportList.add(rowData); } return exportList; } /** * 利用反射批量解析字段 * * @param rowData * @param o * @param c */ private void autoFillRowData(List<String> rowData, Object o, Class<PrisonMemberDo> c) { Field[] fields = c.getDeclaredFields(); // 循环遍历字段,获取字段相应的属性值 for (Field field : fields) { // 假设不为空。设置可见性,然后返回 field.setAccessible(true); try { // 设置字段可见,就可以用get方法获取属性值。 if (!field.getName().equals("id")) { //特殊字段处理数据库中保存的是int,根据字典表切换为对应汉字 if (field.getType() == Date.class) { rowData.add(sdf.format(new Date(field.get(o).toString()))); } else if (codeMap.get(field.getName()) != null) { CodeDo code = codeMapper.parserCodeName(codeMap.get(field.getName()), field.get(o).toString()); rowData.add(code == null ? "未定义" : code.getCodeName()); } else { rowData.add(field.get(o).toString()); } } } catch (Exception e) { log.error("反射获取导出对象的成员域失败!", e); } } }
二、利用反射动态封装数据对象Do成员变量(PrisonMemberDo)
/** * 人员导入 * * @param file * @return */ @Override public Integer upload(MultipartFile file) { Sheet sheet = null; try { sheet = POIUtils.parseSheet(file, 0); } catch (final java.io.IOException e) { e.printStackTrace(); } int num = 0; for (int i = 3; i <= sheet.getLastRowNum(); i++) { final Row row = sheet.getRow(i); if (null != row && row.getCell(0) != null && StringUtils.notEmpty(row.getCell(0).toString())) { num = parserExcel(row, num); } } return num; } /** * 解析Excel文件 * * @param row * @param num * @return */ private int parserExcel(Row row, int num) { final PrisonMemberDo memberDo = new PrisonMemberDo(); try { final String name = POIUtils.parseStr(row.getCell(2)); final String memCode = POIUtils.parseStr(row.getCell(1)); if (StringUtils.isEmpty(memCode) || StringUtils.isEmpty(name)) { return num; } if(prisonMemberMapper.selectSameCode(memCode) != null){ return num; } Field[] fields = memberDo.getClass().getDeclaredFields(); int cellIndex = 0; String cellVal = ""; // 循环遍历字段,获取字段相应的属性值 for (Field field : fields) { // 假设不为空。设置可见性,然后返回 field.setAccessible(true); cellVal = POIUtils.parseStr(row.getCell(cellIndex)); try { // 设置字段可见,就可以用get方法获取属性值。 if (!field.getName().equals("id")&&!field.getName().equals("updatetime")) { //特殊类型特殊处理 Date的日期格式限定为'yyyy-MM-dd',字典表汉字向数字转化 if (field.getType() == Date.class) { getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Object[]{sdf.parse(cellVal)}); } else if (codeMap.get(field.getName()) != null) { CodeDo code = codeMapper.parserCodeVal(codeMap.get(field.getName()), POIUtils.parseStr(row.getCell(cellIndex))); if(code!=null){ getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Integer[]{Integer.parseInt(code.getCode().toString())}); } } else { getSetMethod(memberDo.getClass(), field.getName()).invoke(memberDo, new Object[]{cellVal}); } } } catch (Exception e) { log.error("反射获取并解析对象的成员域失败!", e); } cellIndex++; } memberDo.setUpdatetime(new Date()); prisonMemberMapper.insertSelective(memberDo); num++; return num; } catch (final Exception e) { log.error("解析Excel表格失败!", e); return num; } } /** * 根据成员变量名获取对应set方法 * @param objectClass * @param fieldName * @return */ public static Method getSetMethod(Class objectClass, String fieldName) { try { Class[] parameterTypes = new Class[1]; Field field = objectClass.getDeclaredField(fieldName); parameterTypes[0] = field.getType(); StringBuffer sb = new StringBuffer(); sb.append("set"); sb.append(fieldName.substring(0, 1).toUpperCase()); sb.append(fieldName.substring(1)); Method method = objectClass.getMethod(sb.toString(), parameterTypes); return method; } catch (Exception e) { e.printStackTrace(); } return null; }
备注:
1、成员变量的动态解析需要注意Excel的头部数据定义顺序和成员变量顺序是否一致,如果不一致,会导致数据封装异常!对于自定义的字段需要跨过(like:序号)
2、对于占位符和标题需要跨过,cell的下标起始点应与实际Excel中有效数据保持一致,如果Excel中未展示数据库中所有字段,需根据成员变量名做过滤。
Excel头部定义
public static final String FILE_NAME_PRISON_MEM = "司法厅服刑人员信息"; public static final String TITLE_PRISON_MEM = "司法厅服刑人员信息"; public static final String[] HEADER_PRISON_MEM = new String[] { "序号", "编码","姓名","组织编码","部门名称","性别","民族","出生日期", "证件类型","证件号码","文化程度", "原政治面貌", "户籍所在地","固定居住地","家庭电话","移动电话","婚姻状况","判决书号","判决机关","判决日期","罪名","刑种","原判刑期","刑期起始日期","刑期结束日期","附加刑", "刑期变动","曾受惩处","主要联系人","主要联系人地址","主要联系人电话","相互关系","备注", "更新时间" };
数据对象Do
package cn.showclear.www.pojo.base; import java.util.Date; public class PrisonMemberDo { /** ID */ private Integer id; /** 编码(可唯一标识) */ private String code; /** 姓名 */ private String name; /** 组织编码 */ private String orgCode; /** + 部门名称 */ private String deptName; /** 性别(1:男 2“女) */ private Integer sex; /** 民族 */ private String nation; /** 出生日期 */ private Date birthDate; /** 证件类型,包括1(居民身份证)、2(军人身份证件)、3(武警身份证件)、9(其他)。 */ private Integer paperType; /** 证件号码 */ private String paperNumber; /** 文化程度(1:文盲 2:小学 3:初中 4:高中(含中专,技校)5:大专 6:本科 7:硕士 8 :博士 9:其他) */ private Integer degreeType; /** 原政治面貌(1:中共党员 :2:中共预备党员 3:共青团员 :4:民革党员 5:民盟盟员 6:民建会员 7:民进会员 8:农工党党员 9:致公党党员 10:九三学社社员 11:台盟盟员 12:群众) */ private Integer politicalStatusOld; /** 户籍所在地 */ private String registerAddress; /** 固定居住地 */ private String fixedAddress; /** 家庭电话 */ private String homePhone; /** 移动电话 */ private String mobilePhone; /** 婚姻状况(1:未婚 2:已婚 3:离异 4:丧偶) */ private Integer maritalStatus; /** 判决书号 */ private String sentenceNumber; /** 判决机关 */ private String sentenceUnit; /** 判决日期 */ private Date sentenceDate; /** 罪名 */ private String charge; /** 刑种(1:死缓 2:无期 3:有期 4:拘役 5:管制) */ private Integer punishmentKind; /** 原判刑期 */ private String prisonTerm; /** 刑期起始日期 */ private Date prisonBeginDate; /** 刑期结束日期 */ private Date prisonEndDate; /** 附加刑 */ private String punishmentOther; /** 刑期变动 */ private String punishmentChange; /** 曾受惩处 */ private String punished; /** 主要联系人 */ private String mainContactName; /** 主要联系人地址 */ private String mainContactAddr; /** 主要联系人电话 */ private String mainContactTel; /** 相互关系 */ private String interrelation; /** 备注 */ private String remark; /** 更新时间 */ private Date updatetime; public PrisonMemberDo() { } //...get && set }
三、反射实例
遍历成员变量
/** * 利用反射批量解析字段 * * @param rowData * @param o * @param c */ private void autoFillRowData(List<String> rowData, Object o, Class<PrisonMemberDo> c) { Field[] fields = c.getDeclaredFields(); // 循环遍历字段,获取字段相应的属性值 for (Field field : fields) { // 假设不为空。设置可见性,然后返回 field.setAccessible(true); try { // 设置字段可见,就可以用get方法获取属性值。 if (!field.getName().equals("id")) { if (field.getType() == Date.class) { rowData.add(sdf.format(new Date(field.get(o).toString()))); } else if (codeMap.get(field.getName()) != null) { CodeDo code = codeMapper.parserCodeName(codeMap.get(field.getName()), field.get(o).toString()); rowData.add(code == null ? "未定义" : code.getCodeName()); } else { rowData.add(field.get(o).toString()); } } } catch (Exception e) { log.error("反射获取导出对象的成员域失败!", e); } } }
获取Set和Get方法
/** * java反射bean的get和set方法 * * @param objectClass * @param fieldName * @param fnType "get" or "set" * @return */ @SuppressWarnings("unchecked") public static Method getFnMethod(Class objectClass, String fieldName,String fnType) { StringBuffer sb = new StringBuffer(); sb.append(fnType); sb.append(fieldName.substring(0, 1).toUpperCase()); sb.append(fieldName.substring(1)); try { return objectClass.getMethod(sb.toString()); } catch (Exception e) { } return null; }
执行set或get方法
/** * 执行方法 * * @param o 执行对象 * @param fieldName 属性 * @param value 值 * @param fnType "get" or "set" */ public static void invokeFn(Object o, String fieldName, Object value,String fnType) { Method method = getFnMethod(o.getClass(), fieldName,fnType); try { //指定对象的成员类型,可以切换Object为对应类型进行Set和Get method.invoke(o, new Object[] { value }); } catch (Exception e) { e.printStackTrace(); } }