Java实操 -> 基于注解、反射实现Excel导出动态合并
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
一、效果演示及相关说明
由于项目信息不能泄露,这里采用测试数据,下面的测试数据是手动输入的,仅用来辅助说明下面的解释
测试数据原始效果
合并后的效果
二、首先创建注解类。
作用:加载导出字段上,order 表示分组次数。0表示一次分组,1表示二次分组,依次类推
isflag 表示分组策略,如果为true,则用来这个字段作为当前合并的依据,所以和该字段order相同的字段都会合并
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author hhb
* @date :2021/9/16 13:58
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MergeFlag {
int order() default 0;
boolean isflag() default false;
}
三、自定义合并策略
这里使用的是alibabaEasyExcel的Excel处理框架,相关导出、导入实现如果不懂请查看官方文档
https://www.yuque.com/easyexcel/doc/easyexcel
根据官方提供的单次合并策略OnceAbsoluteMergeStrategy可知,实现合并的核心代码如下
CellRangeAddress cellRangeAddress = new CellRangeAddress(this.firstRowIndex, this.lastRowIndex, this.firstColumnIndex, this.lastColumnIndex);
writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress);
所以我们接下来需要想办法先知道那些数据是需要合并的,所以我们要预处理导出的数据.
然后根据数据上面的注解解析出一个合并map,map的key是 order,value是一个对象,包含通过反射得到的Field[]数组的index索引、合并标识。
详细见代码
/**
* 生成合并策略Map
* @param dataList
* @return
*/
private void generateMergeMap(List dataList,Class<?> type){
//解析分组依据
List<MergeFlagDTO> mergeFlagDTOList = this.analyzeData(type);
Object preObj=null;
for (int i=0;i<dataList.size();i++){
//获取合并表示
Object currObj=dataList.get(i);
if (null!=preObj){
//处理数据
try {
this.handData(0,mergeFlagDTOList,currObj,preObj,i);
} catch (IllegalAccessException e) {
throw new BadRequestAlertException("导出时发生解析错误!");
}
}
preObj=currObj;
}
}
/**
* 解析数据
* @param type
* @return
*/
private List<MergeFlagDTO> analyzeData(Class<?> type) {
Map<Integer, MergeFlagDTO> mergeOrderMap=new HashMap<>();
Field[] fields = type.getDeclaredFields();
//获取注解列表
for (int i=0;i<fields.length;i++){
MergeFlag mergeFlag = fields[i].getAnnotation(MergeFlag.class);
if (null!=mergeFlag){
//加入索引
MergeFlagDTO dto =mergeOrderMap.get(mergeFlag.order())==null?new MergeFlagDTO():mergeOrderMap.get(mergeFlag.order());
dto.getMergeIndexs().add(i);
dto.setOrder(mergeFlag.order());
if (mergeFlag.isflag()){
dto.setField(fields[i]);
}
mergeOrderMap.put(mergeFlag.order(),dto);
}
}
return new ArrayList<>(mergeOrderMap.values()).stream().filter(item -> null != item.getField()).sorted(Comparator.comparing(MergeFlagDTO::getOrder)).collect(Collectors.toList());
}
/**
* 处理数据
* @param start
* @param mergeFlagDTOList
* @param currObj
* @param preObj
* @param index
* @throws IllegalAccessException
*/
private void handData(int start, List<MergeFlagDTO> mergeFlagDTOList, Object currObj, Object preObj, int index) throws IllegalAccessException {
if (start<mergeFlagDTOList.size()){
MergeFlagDTO dto = mergeFlagDTOList.get(start);
Field field = dto.getField();
field.setAccessible(true);
Object currValue = field.get(currObj);
Object preValue = field.get(preObj);
if (currValue.equals(preValue)) {
this.fillMergeMap(dto.getMergeIndexs(),index);
//继续递归
this.handData(start+1,mergeFlagDTOList,currObj,preObj,index);
}
}
}
/**
* 填充合并map
* @param key
* @param index
*/
private void fillMergeMap(Integer key, Integer index){
List<RowRangeDTO> rangeDTOS = mergeMap.get(key)==null?new ArrayList<>():mergeMap.get(key);
//判断是否需要新加
boolean needAdd=true;
//遍历
for (RowRangeDTO rangeDTO:rangeDTOS){
if (rangeDTO.getEndIndex().equals(index)){
rangeDTO.setEndIndex(index+1);
needAdd=false;
}
}
//如果没有匹配上的就说明需要
if (needAdd){
rangeDTOS.add(new RowRangeDTO(index,index+1));
}
mergeMap.put(key,rangeDTOS);
}
最后根据合并map对数据进行合并操作
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer integer){
//每一个cell只合并一次
if (cell.getRowIndex()==1&&cell.getColumnIndex()==0){
mergeMap.keySet().forEach(key-> mergeMap.get(key).forEach(rowRangeDTO -> sheet.addMergedRegionUnsafe(new CellRangeAddress(rowRangeDTO.getStartIndex(),rowRangeDTO.getEndIndex(),key,key))));
}
}
作者 | 樱岛麻衣Ss
来源 | cnblogs.com/ydmysm/p/merge_excel.html
加锋哥微信: java3459 围观锋哥朋友圈,每天推送Java干货!
评论