摘要
Excel导出模版自动加标准,客户不乱改,省心又省力!
正文
导出来 Excel 模版自动生成标准,防止客户不断改动
一句话汇总
Excel 导出来、导进时,依据注释全自动加上表格中认证标准,防止客户因填好不正确的枚举类型字段名而不断改动 Excel
要求情况
针对 Java Web 新项目,一直难以避免的发生 Excel 导进、导出来的要求,而 Excel 导入导出时,枚举类型字段名和枚举值的投射是十分普遍的一种状况
比如:下边这张实例中的性別列
数据库表构造:
Excel 中客户必须键入:男,女,不明
普遍的 Excel 架构都早已遮盖了枚举类型投射的作用,比如:EasyPOI
可是这类实际操作方法针对客户而言,并并不是很便捷,设想一下:倘若客户在性別列键入了:男士,最后的結果一般便是程序流程抛出异常,客户获得提醒:性別键入不正确,暖心的开发人员很有可能会再加上:输入您 男 女 不明,做的更强一些的 很有可能在列头加上标识提醒:该列仅能键入 男 女 不明,可是这类弱限定也没法从源头上解决困难
更强一点的解决方法是:运用 Excel 的数据验证作用,把表格中再加上标准校检,让客户只有键入恰当的枚举值,防止因一次键入不正确而不断返修,消耗客户的時间和好心态
当客户键入了非枚举值以后,Excel 会提醒客户键入不合规管理,严禁客户储存
那样的互动就能从根源确保客户键入恰当的值
那那么友善的设计方案,在 Java 中怎样能便捷且扩展性更强的完成呢?
要求完成
我这边的完成是根据 EasyPOI 注释(EasyPOI 变换投射关联注释重复使用) 反射面 完成的,解决了之上要求困扰的与此同时,能够达到编码一处改动,好几个作用都起效的目地
代码仓库
GayHub
dao层
@Excel 注释中的 replace 特性,该特性是 EasyPOI 用于做字段名投射的,我这里重复使用他做 Excel 认证的选择项,此外一个便是 orderNum 特性,用该值来全自动获得某一字段名在 Excel 中的列的部位
@Data
public class Human extends BaseEntity {
private Long id;
@Excel(name = "名字", orderNum = "1", width = 15)
private String name;
@Excel(name = "年纪", orderNum = "2", width = 15)
private Integer age;
@Excel(name = "性別", replace = {"男_1", "女_2", "不明_3"}, orderNum = "3", width = 15)
private Integer gender;
}
获得字段名和列部位的投射
此类在复位时,必须特定当今导出来 Excel 相匹配的dao层的类种类,随后根据解析xml类中字段名的注释,转化成字段名和列排列(部位)的投射关联
public class FieldOrderMappingHelper<T> {
/**
* 适用的较大字段名数
*/
private final static int MAX_LIST_SIZE = 26;
public FieldOrderMappingHelper(Class<T> pojo) {
this.pojo = pojo;
initMap();
}
/**
* 分析注解的 pojo 目标
*/
private Class<T> pojo;
/**
* 字段名和编号的投射关联
*/
private HashMap<String, Integer> fieldAndOrderMap;
/**
* 作用:复位类的字段名內容,创建字段名和编号及其字段名和 excel 字段名的投射关联
*
* @author kangshuai@gridsum.com
* @date 2021/4/9 12:06
*/
private void initMap() {
HashMap<String, Integer> fieldAndOrderMap = new HashMap<>(16);
HashSet<Integer> existOrderNumSet = new HashSet<>(16);
List<FiledAndOrder> list = new ArrayList<>();
list = initList(list, pojo);
if (list.size() > MAX_LIST_SIZE) {
throw new RuntimeException(pojo.getName() "现阶段较大适用 26 个字段名,26 必须改编码");
}
// 排列
list.sort(Comparator.comparing(FiledAndOrder::getOrder));
for (int i = 0; i < list.size(); i ) {
if (existOrderNumSet.contains(list.get(i).getOrder())) {
throw new RuntimeException(pojo.getName() "类內部或与父类字段名中存有反复的 excel 排列,请改动");
}
existOrderNumSet.add(list.get(i).getOrder());
fieldAndOrderMap.put(list.get(i).getFiledName(), i);
}
this.fieldAndOrderMap = fieldAndOrderMap;
}
/**
* 作用:复位类的字段名信息内容,转化成 ArrayList
*
* @return java.util.List<com.gridsum.ad.ooh.project.entity.FiledAndOrder>
* @author kangshuai@gridsum.com
* @date 2021/4/9 12:09
*/
private List<FiledAndOrder> initList(List<FiledAndOrder> list, Class<?> pojoClass) {
if (Object.class.equals(pojoClass)) {
return list;
}
Field[] fields = pojoClass.getDeclaredFields();
for (Field f : fields) {
// 寻找全部加了 Excel 注释的字段名
Excel annotation = f.getAnnotation(Excel.class);
if (annotation == null) {
continue;
}
// 过虑掩藏行
if (annotation.isColumnHidden()) {
continue;
}
FiledAndOrder filedAndOrder = new FiledAndOrder(f.getName(), Integer.parseInt(annotation.orderNum()));
list.add(filedAndOrder);
}
// 递归算法搜索父类
Class<?> superclass = pojoClass.getSuperclass();
return initList(list, superclass);
}
public HashMap<String, Integer> getFieldAndOrderMap() {
return fieldAndOrderMap;
}
}
设定认证标准
setValidation 方式有两个主要参数,第一个是导出来 Excel 相匹配的dao层的类种类,第二个是 FieldOrderMappingHelper.getFieldAndOrderMap() 获得到的字段名和排列投射,此类根据反射面字段名上的注释,全自动为转化成的 workbook 加上认证标准
public class ExcelStyleHelper {
/**
* 加上列值认证的最少行
*/
public static final int EXCEL_VALID_ROW_MIN = 1;
/**
* 加上列值认证的较大行
*/
public static final int EXCEL_VALID_ROW_MAX = (2 << 15) - 1;
/**
* Excel 目标
*/
private Workbook workbook;
/**
* Sheet 页,默认设置取第一个 sheet 页
*/
private Sheet sheet;
public ExcelStyleHelper(Workbook workbook) {
this.workbook = workbook;
this.sheet = workbook.getSheetAt(0);
}
/**
* 作用:表格中加上下拉列表,仅适用 xls
*
* @author kangshuai@gridsum.com
* @date 2021/4/8 18:55
*/
public void setValidation(Class<?> pojoClass, HashMap<String, Integer> map) {
// 递归算法到 Object 就停住
if (Object.class.equals(pojoClass)) {
return;
}
// 获得所有的字段
Field[] fields = pojoClass.getDeclaredFields();
for (Field field : fields) {
Excel annotation = field.getAnnotation(Excel.class);
if (annotation == null) {
continue;
}
String[] replace = annotation.replace();
if (replace.length == 0) {
continue;
}
String[] textList = new String[replace.length];
for (int i = 0; i < replace.length; i ) {
textList[i] = replace[i].split("_")[0];
}
// 依据列名获得他在 excel 中的行数(融合 excel 注释中的排列)
Integer col = map.get(field.getName());
setValid(textList, col, col);
}
// 递归算法父类的注释
Class<?> superclass = pojoClass.getSuperclass();
setValidation(superclass, map);
}
/**
* 作用:设定认证区段
*
* @author kangshuai@gridsum.com
* @date 2021/4/9 15:11
*/
private void setValid(String[] textList, int firstCol, int endCol) {
// 设定数据有效性载入在哪个表格中上,四个主要参数分别是:起止行、停止行、起止列、停止列
CellRangeAddressList regions = new CellRangeAddressList(EXCEL_VALID_ROW_MIN, EXCEL_VALID_ROW_MAX, firstCol, endCol);
// 载入下拉框內容
DVConstraint constraint = DVConstraint.createExplicitListConstraint(textList);
// 数据有效性目标
HSSFDataValidation dataList = new HSSFDataValidation(regions, constraint);
sheet.addValidationData(dataList);
}
}
实例导出来编码
操纵层编码以下
@RestController
public class ExcelExportController {
@GetMapping("excel")
public void excelExport(HttpServletResponse response) throws Exception {
List<Human> humanList = new ArrayList<>();
doWriteListToResponse(humanList, Human.class, response, "检测 Sheet", "检测 Excel.xls");
}
/**
* 作用:将結果载入輸出流
*
* @author kangshuai@gridsum.com
* @date 2021/4/14 14:46
*/
public <T> void doWriteListToResponse(List<T> list, Class<T> exportType, HttpServletResponse response, String sheetName, String excelName) throws IOException {
ExportParams ex = new ExportParams(null, sheetName, ExcelType.HSSF);
// 建立导出来目标
Workbook workbook = ExcelExportUtil.exportExcel(ex, exportType, list);
// 复位java工具
HashMap<String, Integer> map = new FieldOrderMappingHelper<>(exportType).getFieldAndOrderMap();
ExcelStyleHelper styleHelper = new ExcelStyleHelper(workbook);
// 加上标准
styleHelper.setValidation(exportType, map);
// 载入輸出流,忽视这里硬编码
response.setHeader("Content-Disposition", "attachment;filename=" new String(excelName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
response.setCharacterEncoding("UTF-8");
response.setContentType("application/x-download");
workbook.write(response.getOutputStream());
}
}
关注不迷路
扫码下方二维码,关注宇凡盒子公众号,免费获取最新技术内幕!
评论0