最近使用EasyExcel,实现每一个类型在一个sheet上,合并成一个excel,写一点心得
导出
导出主要配置在entity上,主要的是ExcelProperty的value 如果相同的名称会合并,以此来实现字段多种样式组合
@Data
@EqualsAndHashCode(callSuper = false)
//一些表头基本配置
@ContentStyle(horizontalAlignment= HorizontalAlignment.CENTER)
@HeadStyle(horizontalAlignment= HorizontalAlignment.CENTER)
@ContentFontStyle(fontHeightInPoints=12)
@HeadFontStyle(fontHeightInPoints=12)
public class demo implements Serializable{
//第一行标题 因为相同,会合并单元格
@ExcelProperty(index = 0, value = {"第一行标题","name1名称"})
@ColumnWidth(16)
private String name1;
@ExcelProperty(index = 1, value = {"第一行标题","name2名称"})
@ColumnWidth(8)
private String name2;
}
@ApiOperation(value = "导出【excel】", httpMethod = "GET")
@GetMapping(path = "/download")
public void download(@ApiParam("导出查询对象") Dto dto, HttpServletResponse response) throws IOException {
try {
String fileName = URLEncoder.encode("表名-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()), "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build();
excelWriter.write(service1.list(dto), EasyExcelFactory.writerSheet(0, "name1").head(Entity1.class).build());
excelWriter.write(service2.list(dto), EasyExcelFactory.writerSheet(1, , "name2").head(Entity2.class).build());
excelWriter.write(service3.list(dto), EasyExcelFactory.writerSheet(2, , "name3").head(Entity3.class).build());
excelWriter.write(service4.list(dto), EasyExcelFactory.writerSheet(3,, "name4").head(Entity4.class).build());
excelWriter.write(service5.list(dto), EasyExcelFactory.writerSheet(4, , "name5").head(Entity5.class).build());
excelWriter.write(service6.list(dto), EasyExcelFactory.writerSheet(5, , "name6").head(Entity6.class).build());
excelWriter.finish();
} catch (Exception e) {
e.printStackTrace();
}
}
导入
导入就比较繁琐
1、excel设计
- 格式处理:文本就是文本,时间就是时间,数字就是数字,设定好格式
- 设定好规则,如下图可以控制输入和进行提示,注意 下拉框 用序列,字段用’,'隔开
2、导入Converter配置
用于解决数据输入输出时的转换,比如输入名称,转换成编码
public class AreaConverter implements Converter<String> {
private static List<ConverterDto> areaConverterDtos = null;
public AreaConverter() {
ConverterService service = SpringApplicationUtils.getBean(ConverterService.class);
if (CollectionUtil.isEmpty(areaConverterDtos)) {
areaConverterDtos = service.areaListByLevel();
}
}
@Override
public Class supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
@Override
//导入
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
for (ConverterDto dto : areaConverterDtos) {
if (dto.getName().contains(cellData.getStringValue()) ) {
return dto.getCode();
}
}
return "-1";
}
@Override
//导出
public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
for (ConverterDto dto : areaConverterDtos) {
if (value.equals(dto.getCode())) {
return new CellData(dto.getName());
}
}
return new CellData("未知");
}
}
@Component
//通过上下文获取到实例bean
public class SpringApplicationUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
/**
* 获取applicationContext
*
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringApplicationUtils.applicationContext == null) {
SpringApplicationUtils.applicationContext = applicationContext;
}
}
/**
* 通过name获取 Bean.
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean.
*
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
*
* @param name
* @param clazz
* @param <T>
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
/**
* 获取指定类型的所有bean实例
*
* @param clazz
* @param <T>
* @return
*/
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
return getApplicationContext().getBeansOfType(clazz);
}
}
3、实现AnalysisEventListener
AnalysisEventListener功能很强大
@Override
public void invoke(Dto dto, AnalysisContext context) {
//每一条数据都会循环,可以实现空数据,重复数据的检查
//在外层增加list,就可以实现数据临时的缓存
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//通过变量list 也可以实现检查,并且能实现批量存储
}
个人不太这个模块的定性,一方面,它可以处理空数据的检查,重复数据的检查等一些数据筛查工作。另一方面,也可以做数据的存储,如果在此做数据存储,那么数据的转换也是可以在这里完成的。
我没更加深入的理解,就先实现了用途,如开始所说,我需要在一个excel中,导入多个sheet并且类型不一,每个类型还要存入多个表,如果不使用listener,代码会异常臃肿难看。
贴下并不完整代码:
public class ExcelListener extends AnalysisEventListener<ExcelBaseModel> {
Service service;
Map<Integer, String> checkNullMap;
List<FmmpXmxx> list1 = new ArrayList<>();
List<Object> list2 = new ArrayList<>();
public ExcelListener() {
service = SpringApplicationUtils.getBean(Service.class);
checkNullMap = new HashMap<>();
}
//new的时候带进来 checkNullMap 查空字符,其实其他参数也可以带进来,所以非常灵活
public ExcelListener(Map<Integer, String> checkNullMap) {
service = SpringApplicationUtils.getBean(Service.class);
this.checkNullMap = checkNullMap;
}
@Override
public void invoke(ExcelBaseModel dto, AnalysisContext context) {
this.checkNull(context);
this.buildEntity(dto);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (!list1.isEmpty()) {
this.checkIdIsExist(context); //去重一方面插入数据本身重复问题,另一方面,每个sheet之间重复问题,还有excel和数据库的重复
//很多人会先在list到达一定数量后,在invoke做保存,然后在这里做剩下的数据保存,我因为业务不同没有做此处理,并且个人更加倾向保存逻辑都在该方法下完成。可以通过list下标实现
service.saveBatch(list1);
service.saveBatchZb(list2);
}
}
public void checkNull(AnalysisContext context) {
Map<Integer, Cell> cellMap = context.readSheetHolder().getCellMap();
checkNullMap.keySet().forEach(colNum -> {
if (!cellMap.containsKey(colNum)) {
int row = context.readRowHolder().getRowIndex() + 1;
throw new ServiceException(400, context.readSheetHolder().getSheetName() + ":第" + row + "行 " + checkNullMap.get(colNum) + "为空,请核实");
}
});
}
private void checkIdIsExist(AnalysisContext context) {
HashSet<String> baseIds = service.list().stream().map(FmmpXmxx::getId).collect(Collectors.toCollection(HashSet::new));
HashSet<String> targetIds = list1.stream().map(FmmpXmxx::getId).collect(Collectors.toCollection(HashSet::new));
if (targetIds.size() != list1.size()) {
throw new ServiceException(400, context.readSheetHolder().getSheetName() + ": " + checkNullMap.get(0) + " 有重复");
}
list1.forEach(index -> {
if (baseIds.contains(index.getId())) {
throw new ServiceException(400, context.readSheetHolder().getSheetName() + ":第" + context.readRowHolder().getRowIndex() + 1 + "行 " + checkNullMap.get(0) + " 已经存在");
}
});
}
private void buildEntity(ExcelBaseModel dto) {
//dto处理后插入list 这里dto是做了处理的,不展示逻辑代码了
list1.add(dto);
list2.add(dto);
}
}
4、业务逻辑
这里就会显得比较简洁,毕竟真正的处理都在listener里了
@Override
@Transactional //在这里配置事务,保证业务上出错全部回滚
public boolean excel2Database(MultipartFile file) {
Map<Integer, String> checkNullMap = new HashMap<>(5);
checkNullMap.put(0, "字段名1-默认是主键名,判断重复");
checkNullMap.put(1, "字段名2");
checkNullMap.put(2, "字段名3");
checkNullMap.put(7, "字段名4");
checkNullMap.put(8, "字段名5");
try {
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity1.class).sheet(0).doReadSync();
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity2.class).sheet(1).doReadSync();
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity3.class).sheet(2).doReadSync();
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity4.class).sheet(3).doReadSync();
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity5.class).sheet(4).doReadSync();
EasyExcelFactory.read(file.getInputStream()).registerReadListener(new ExcelListener(checkNullMap)).head(Entity6.class).sheet(5).doReadSync();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
相关文章
暂无评论...