springboot-动态表头easyExcel导入导出,2.2.7版本。自定义converter处理日期类型。+其他记录(Date日期处理、SQL写法等)=20210207~

9 篇文章 0 订阅
9 篇文章 0 订阅

一、动态表头easyExcel导出。

上次用easyExcel做了个动态表头导入(https://blog.csdn.net/qq_37337660/article/details/110288393),不过,当时用的版本过低,并且也没有详细说明,这次整了个easyExcel动态表头的导出。
固定表头的有实体类,可以用注解,比较简单(下面参考罗列一些我觉得有用的处理办法),因为业务需要所以整动态表头(表头名称和数目都不固定)。还有经常出的错误

官网:https://www.yuque.com/easyexcel 有问题可以里面找解决办法。

1、加依赖

<!--easyExcel-->这个是必须,版本最新了。
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.7</version>
        </dependency>
        这个和第四个是升级了,支持某些功能和解决报错
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.0</version>
        </dependency>
        这个是解决乱七八糟报错当时
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.0</version>
        </dependency>

2、自己搞了个工具类

package com.trs.ai.ty.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * easyExcel工具类
 *
 * @author li.chengzhen
 * @version 1.0
 * @date 2021/3/2 16:34
 */
public class EasyExcelUtil {

    /**
     * 导出
     *
     * @param response HttpServletResponse
     * @param headFiled 表头
     * @param data 表数据
     */
    public static void export(
            HttpServletResponse response, List<List<String>> headFiled, List<List<Object>> data)
            throws IOException {
        EasyExcel.write(response.getOutputStream())
                .head(headFiled)
                // 样式
                .registerWriteHandler(getHorizontalCellStyleStrategy())
                .autoCloseStream(Boolean.FALSE)
                .sheet(0)
                .doWrite(data);
    }
    /**
     * 设置请求头、文件名
     *
     * @param fileName excel文件名
     */
    public static void setResponse(HttpServletResponse response, String fileName) {
        // 编码设置成UTF-8,excel文件格式为.xlsx
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        // 这里URLEncoder.encode可以防止中文乱码 和easyexcel本身没有关系
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    }

    /**
     * excel首列序号列样式
     *
     * @param workbook Workbook
     * @return org.apache.poi.ss.usermodel.CellStyle
     */
    public static CellStyle firstCellStyle(Workbook workbook) {
        CellStyle cellStyle = workbook.createCellStyle();
        // 居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        cellStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
        // 设置边框
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);
        cellStyle.setBorderTop(BorderStyle.THIN);
        // 文字
        Font font = workbook.createFont();
        font.setBold(Boolean.TRUE);
        cellStyle.setFont(font);
        return cellStyle;
    }

    /**
     * 用于设置excel导出时的样式 easyexcel 导出样式
     *
     * @return com.alibaba.excel.write.style.HorizontalCellStyleStrategy
     */
    public static HorizontalCellStyleStrategy getHorizontalCellStyleStrategy() {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 字体
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 11);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了
        // 背景设置
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.AUTOMATIC.getIndex());
        // 文字
        WriteFont contentWriteFont = new WriteFont();
        contentWriteFont.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 边框
        contentWriteCellStyle.setBorderBottom(BorderStyle.NONE);
        contentWriteCellStyle.setBorderLeft(BorderStyle.NONE);
        contentWriteCellStyle.setBorderRight(BorderStyle.NONE);
        contentWriteCellStyle.setBorderTop(BorderStyle.NONE);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }
}

先调用setResponse(),再调用export()方法,就可以直接返回给前端。

样式什么的都比较简单,可以自己设置,我想说的是不是所有的数据都是string,所以导出的时候就需要特殊处理。

3、自定义converter处理特殊类型:

我数据是直接去数据库查,当有日期的时候,返回的直接就是java.sql.Date。

这里自定义了一个SqlDateConverter ,这个转换器的convertToExcelData()就是导出的时候会调用的方法,我这里定义了Date怎么处理,偷懒直接借鉴easyExcel自带的DateNumberConverter的处理办法。自己处理反而不好搞。

/**
 * sqlDate转换器。将日期转换成Excel的数字形式
 */
@Component
public class SqlDateConverter implements Converter<Date> {

    @Override
    public Class supportJavaTypeKey() {
        return Date.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    @Override
    public Date convertToJavaData(
            CellData cellData,
            ExcelContentProperty contentProperty,
            GlobalConfiguration globalConfiguration) {
        return null;
    }

    @Override
    public CellData convertToExcelData(
            Date value,
            ExcelContentProperty contentProperty,
            GlobalConfiguration globalConfiguration) {
        if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) {
            return new CellData(
                    BigDecimal.valueOf(
                            DateUtil.getExcelDate(
                                    value, globalConfiguration.getUse1904windowing())));
        } else {
            return new CellData(
                    BigDecimal.valueOf(
                            DateUtil.getExcelDate(
                                    value,
                                    contentProperty
                                            .getDateTimeFormatProperty()
                                            .getUse1904windowing())));
        }
    }
}

这样肯定是没法生效的。所以还需要一个加载器,我这里直接定义一个:看我把SqlDateConverter 放在哪就知道了。


import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.converters.DefaultConverterLoader;
import com.alibaba.excel.converters.bigdecimal.BigDecimalBooleanConverter;
import com.alibaba.excel.converters.bigdecimal.BigDecimalNumberConverter;
import com.alibaba.excel.converters.bigdecimal.BigDecimalStringConverter;
import com.alibaba.excel.converters.booleanconverter.BooleanBooleanConverter;
import com.alibaba.excel.converters.booleanconverter.BooleanNumberConverter;
import com.alibaba.excel.converters.booleanconverter.BooleanStringConverter;
import com.alibaba.excel.converters.bytearray.BoxingByteArrayImageConverter;
import com.alibaba.excel.converters.bytearray.ByteArrayImageConverter;
import com.alibaba.excel.converters.byteconverter.ByteBooleanConverter;
import com.alibaba.excel.converters.byteconverter.ByteNumberConverter;
import com.alibaba.excel.converters.byteconverter.ByteStringConverter;
import com.alibaba.excel.converters.date.DateNumberConverter;
import com.alibaba.excel.converters.date.DateStringConverter;
import com.alibaba.excel.converters.doubleconverter.DoubleBooleanConverter;
import com.alibaba.excel.converters.doubleconverter.DoubleNumberConverter;
import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter;
import com.alibaba.excel.converters.file.FileImageConverter;
import com.alibaba.excel.converters.floatconverter.FloatBooleanConverter;
import com.alibaba.excel.converters.floatconverter.FloatNumberConverter;
import com.alibaba.excel.converters.floatconverter.FloatStringConverter;
import com.alibaba.excel.converters.inputstream.InputStreamImageConverter;
import com.alibaba.excel.converters.integer.IntegerBooleanConverter;
import com.alibaba.excel.converters.integer.IntegerNumberConverter;
import com.alibaba.excel.converters.integer.IntegerStringConverter;
import com.alibaba.excel.converters.longconverter.LongBooleanConverter;
import com.alibaba.excel.converters.longconverter.LongNumberConverter;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.converters.shortconverter.ShortBooleanConverter;
import com.alibaba.excel.converters.shortconverter.ShortNumberConverter;
import com.alibaba.excel.converters.shortconverter.ShortStringConverter;
import com.alibaba.excel.converters.string.StringBooleanConverter;
import com.alibaba.excel.converters.string.StringErrorConverter;
import com.alibaba.excel.converters.string.StringNumberConverter;
import com.alibaba.excel.converters.string.StringStringConverter;
import com.alibaba.excel.converters.url.UrlImageConverter;
import com.trs.ai.ty.listener.SqlDateConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * easyExcel:自定义全局加载器
 *
 */
@Configuration
public class CustomerDefaultConverterLoader {
    /** 存放写时用到的converter */
    private static final String ALL_CONVERTER = "allConverter";
    /** 存放所有的converter */
    private static final String WRITE_CONVERTER = "defaultWriteConverter";

    @Bean
    public DefaultConverterLoader init() throws IllegalAccessException {
        DefaultConverterLoader converters = new DefaultConverterLoader();
        Field[] fields = converters.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.getType() == Map.class) {
                Map<String, Converter> oldMap = (Map<String, Converter>) field.get(converters);
                // 兼容高版本(2.2.0+)通过静态代码块初始化 复用旧代码 节省空间
                if (oldMap != null && !oldMap.isEmpty()) {
                    if (WRITE_CONVERTER.equalsIgnoreCase(field.getName())) {
                        putWriteConverter(oldMap, new SqlDateConverter());
                    } else if (ALL_CONVERTER.equalsIgnoreCase(field.getName())) {
                        putAllConverter(oldMap, new SqlDateConverter());
                    }
                    field.set(converters, oldMap);
                } else {
                    setConverter(converters, field);
                }
            }
        }
        return converters;
    }

    private void setConverter(DefaultConverterLoader converters, Field field)
            throws IllegalAccessException {
        if (WRITE_CONVERTER.equalsIgnoreCase(field.getName())) {
            Map<String, Converter> map = new HashMap<>(32);
            putWriteConverter(map, new SqlDateConverter());
            putWriteConverter(map, new BigDecimalNumberConverter());
            putWriteConverter(map, new BooleanBooleanConverter());
            putWriteConverter(map, new ByteNumberConverter());
            putWriteConverter(map, new DateStringConverter());
            putWriteConverter(map, new DoubleNumberConverter());
            putWriteConverter(map, new FloatNumberConverter());
            putWriteConverter(map, new IntegerNumberConverter());
            putWriteConverter(map, new LongNumberConverter());
            putWriteConverter(map, new ShortNumberConverter());
            putWriteConverter(map, new StringStringConverter());
            putWriteConverter(map, new FileImageConverter());
            putWriteConverter(map, new InputStreamImageConverter());
            putWriteConverter(map, new ByteArrayImageConverter());
            putWriteConverter(map, new BoxingByteArrayImageConverter());
            putWriteConverter(map, new UrlImageConverter());
            field.set(converters, map);
        } else if (ALL_CONVERTER.equalsIgnoreCase(field.getName())) {
            Map<String, Converter> map = new HashMap<>(64);
            putAllConverter(map, new SqlDateConverter());
            putAllConverter(map, new BigDecimalBooleanConverter());
            putAllConverter(map, new BigDecimalNumberConverter());
            putAllConverter(map, new BigDecimalStringConverter());

            putAllConverter(map, new BooleanBooleanConverter());
            putAllConverter(map, new BooleanNumberConverter());
            putAllConverter(map, new BooleanStringConverter());

            putAllConverter(map, new ByteBooleanConverter());
            putAllConverter(map, new ByteNumberConverter());
            putAllConverter(map, new ByteStringConverter());

            putAllConverter(map, new DateNumberConverter());
            putAllConverter(map, new DateStringConverter());

            putAllConverter(map, new DoubleBooleanConverter());
            putAllConverter(map, new DoubleNumberConverter());
            putAllConverter(map, new DoubleStringConverter());

            putAllConverter(map, new FloatBooleanConverter());
            putAllConverter(map, new FloatNumberConverter());
            putAllConverter(map, new FloatStringConverter());

            putAllConverter(map, new IntegerBooleanConverter());
            putAllConverter(map, new IntegerNumberConverter());
            putAllConverter(map, new IntegerStringConverter());

            putAllConverter(map, new LongBooleanConverter());
            putAllConverter(map, new LongNumberConverter());
            putAllConverter(map, new LongStringConverter());

            putAllConverter(map, new ShortBooleanConverter());
            putAllConverter(map, new ShortNumberConverter());
            putAllConverter(map, new ShortStringConverter());

            putAllConverter(map, new StringBooleanConverter());
            putAllConverter(map, new StringNumberConverter());
            putAllConverter(map, new StringStringConverter());
            putAllConverter(map, new StringErrorConverter());
            field.set(converters, map);
        }
    }

    private void putWriteConverter(Map<String, Converter> map, Converter converter) {
        map.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter);
    }

    private void putAllConverter(Map<String, Converter> map, Converter converter) {
        map.put(
                ConverterKeyBuild.buildKey(
                        converter.supportJavaTypeKey(), converter.supportExcelTypeKey()),
                converter);
    }
}

到这里,几乎没什么问题了,有问题欢迎询问。不得不说easyExcel真的比较好用,上手也快,升级到现版本已经很能满足我的需求了,当然现在我的数据非常小,以后大了需要处理一下。

4、错误

  1. com.alibaba.excel.exception.ExcelDataConvertException: Can not find 'Converter' support class Timestamp.
    从timestamp到单元格,easyExcel不知道怎么处理。这里选择自定义一个转换器

  2. WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.apache.poi.openxml4j.util.ZipSecureFile$1 (file:/D:/.m2/repository/org/apache/poi/poi-ooxml/3.17/poi-ooxml-3.17.jar) to field java.io.FilterInputStream.in WARNING: Please consider reporting this to the maintainers of org.apache.poi.openxml4j.util.ZipSecureFile$1 WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release。这个警告是导入的时候会提示
    通过升级子依赖解决。开头提到了:

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.0</version>
        </dependency>
  1. ERROR 35820 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/wdf] threw exception [Request processing failed; nested exception is com.alibaba.excel.exception.ExcelAnalysisException: java.lang.NoClassDefFoundError: org/apache/poi/POIXMLTypeLoader] with root cause
    java.lang.ClassNotFoundException: org.apache.poi.POIXMLTypeLoader
    添加了上面依赖就报这个错,然后再加入以下依赖解决:
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.0</version>
        </dependency>

参考:官网
easyExcel导出 无模版 实体类 导出 自定义表头
EasyExcel自定义Converter全局加载器以及加载Converter的个人总结
报错处理:An illegal reflective access operation has occurred Apache POI
使用 EasyExcel 读取Excel(两种方式)
阿里的Easyexcel读取Excel文件(最新版本)
Spring Boot整合EasyExcel(完整版包含上传解析excel和下载模板)

二、其他记录

  1. JSONArray转Map的三种实现方式

  2. 查询数据时,如果某列出现重复,则取该列的最新记录

  3. java Date和数据库中各种时间的处理

  4. 用java判断数据库表是否被创建

  5. 问题解决:DatabaseMetaData.getTables()方法,返回了所有库中的表

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,这里是SpringBoot+Mybatis-plus整合easyExcel批量导入Excel数据库+导出Excel的方法。 1. 添加依赖 在 pom.xml 文件中添加以下依赖: ```xml <!-- easyExcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.6</version> </dependency> <!-- mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> ``` 2. 创建实体类 创建一个实体类,用于映射 Excel 表格中的数据。 ```java @Data public class User { @ExcelProperty("姓名") private String name; @ExcelProperty("年龄") private Integer age; @ExcelProperty("性别") private String gender; } ``` 3. 创建Excel读取器 创建一个 Excel 读取器,用于读取 Excel 表格中的数据,并将数据存储到数据库中。 ```java @Component public class ExcelReader { @Autowired private UserService userService; /** * 读取 Excel 表格中的数据,并将数据存储到数据库中 */ public void readExcel(String fileName) { EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet().doRead(); } /** * 用户Excel监听器 */ private class UserExcelListener extends AnalysisEventListener<User> { /** * 每读取一行数据,就会调用该方法 */ @Override public void invoke(User user, AnalysisContext context) { userService.save(user); } /** * 读取完整个 Excel 表格后,会调用该方法 */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // do nothing } } } ``` 4. 创建Excel导出器 创建一个 Excel 导出器,用于从数据库中获取数据,并将数据导出Excel 表格中。 ```java @Component public class ExcelWriter { @Autowired private UserService userService; /** * 将用户数据导出Excel 表格中 */ public void writeExcel(String fileName) { List<User> userList = userService.list(); EasyExcel.write(fileName, User.class).sheet().doWrite(userList); } } ``` 5. 创建Controller 创建一个 Controller,用于接收前端请求,并调用相应的方法处理请求。 ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private ExcelReader excelReader; @Autowired private ExcelWriter excelWriter; /** * 批量导入用户数据 */ @PostMapping("/import") public void importExcel(@RequestParam("file") MultipartFile file) throws IOException { excelReader.readExcel(file.getInputStream()); } /** * 导出用户数据到Excel */ @GetMapping("/export") public void exportExcel(HttpServletResponse response) throws IOException { String fileName = "用户信息.xlsx"; response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); response.setContentType("application/vnd.ms-excel"); excelWriter.writeExcel(response.getOutputStream()); } } ``` 6. 配置文件 在 application.yml 文件中添加数据库连接信息。 ```yaml spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: root mybatis-plus: mapper-locations: classpath:/mapper/*.xml type-aliases-package: com.example.demo.domain ``` 7. 测试 启动应用程序,并在浏览器中访问以下地址: - http://localhost:8080/user/export :导出 Excel 表格 - http://localhost:8080/user/import :导入 Excel 表格 以上就是 SpringBoot+Mybatis-plus整合easyExcel批量导入Excel数据库+导出Excel 的方法了,希望能帮到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值