Spring-Boot-Starter quickly implements the method of Excel import and export

Self introduction

IT Guoguo
is an ordinary technical house, welcome to like, follow and forward, please take care of me.

Getting Started and Installation

Introduction

In order to meet the function of quickly implementing excel import and export in the project, the excel open source tool easypoi is integrated into spring-boot-starter, which reduces some repetitive codes while achieving no intrusion into easypoi. For example:

  • Enhanced the function of easypoi for repeated verification of field values;

  • Improved the convenience and efficiency of dictionary conversion;

  • Added the function of linkage conversion between fields;

These scenarios are also the function points that I often use in my usual projects. In order to avoid repeated wheel creation and reduce redundant code, I wrote an easypoix-spring-boot-starter extension jar package.

Install

  • Reference maven dependencies in your maven project

<dependency>
    <groupId>com.itguoguo</groupId>
    <artifactId>easypoix-spring-boot-starter</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>
  • Add dictionary prefix to your spring boot project configuration file (optional)

itguoguo.easypoix.dictPrefix=basic:dict:

tool

spring-boot-starter

The core of SpringBoot is several annotations: SpringBootConfiguration, EnableAutoConfiguration, ComponentScan , relying on these annotations to complete the so-called automatic assembly function, this automatic assembly is simply to inject the beans you need into the Spring container. (The annotations on the SpringBootApplication startup class are simply a combination of SpringBootConfiguration, EnableAutoConfiguration, and ComponentScan annotations to avoid developers adding them one by one).

The SpringBoot program will parse three annotations of SpringBootConfiguration, EnableAutoConfiguration, and ComponentScan during startup :

  • SpringBootConfiguration: Contains Configuration annotations to implement configuration files

  • ComponentScan: Specify the scan range

  • EnableAutoConfiguration: You can know from the source code that this annotation uses Import to introduce the AutoConfigurationImportSelector class, and the AutoConfigurationImportSelector class loads the spring.factories file under the MATE-INF folder of all jar packages through SpringFactortisLoader. spring.factories contains all XXXConfiguration classes that need to be assembled The fully qualified name of the . The XXXConfiguration class contains the information needed to instantiate this class. For example, if this is a data source Configuration class, then there should be information such as database driver, user name, password, and so on.

Spring Boot will do these things when it starts :

  • When Spring Boot starts, it will look for the resources/META-INF/spring.factories file in the dependent Starter package, and then scan the Jar package that the project depends on according to the Jar package configured in the file.

  • Load the AutoConfigure class according to the spring.factories configuration

  • According to the conditions of the @Conditional annotation, auto-configure and inject the Bean into the Spring Context

Summary :

SpringBoot is not as mysterious as imagined, just a few things:
1. Provides a configuration class that defines the instantiation process of the objects we need;
2. Provides a spring.factories file that contains the configuration class Fully qualified name;
3. Package the configuration class and spring.factories file into a starter starter;
4. Load the spring.factories file information in the starter.jar package when the program starts, and then instantiate the classes in the file through reflection.

easypoi

unique features

  • Annotation-based import and export, you can modify Excel by modifying annotations

  • Support common style customization

  • Header fields that can be flexibly defined based on map

  • Support one-to-many export and import

  • Support template export, some common tags, custom tags

  • Support HTML/Excel conversion, if the template can not meet the abnormal needs of users, please use this function

  • Support word export, support pictures, Excel

use

  • 1. easypoi parent package – everyone knows the function

  • 2. easypoi-annotation basic annotation package, acting on entity objects, facilitates maven multi-project dependency management after splitting

  • 3. The easypoi-base import and export toolkit can complete Excel export, import, Word export, and Excel export functions

  • 4.easypoi-web is coupled with spring-mvc based on AbstractView, which greatly simplifies the export function under spring-mvc

  • 5. The xercesImpl package is used for sax import (this package may cause strange problems), and the poi-scratchpad is used for word export, both of which are optional packages

Maven coordinates

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-base</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>4.1.0</version>
</dependency>
<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-annotation</artifactId>
    <version>4.1.0</version>
</dependency>

scenes to be used

Our goal is to use a tool method as much as possible to achieve simple export and import Excel functions. Several common scenarios are listed below for reference.

Simple export to Excel

Implementation steps

Create a new export entity model, the @ExcelFileAttr annotation provides the file name of the exported Excel, and the @Excel annotation provides the field of the header, where the name attribute is the field name, and the dict attribute is the keyword constant of the dictionary

@Data
@ExcelFileAttr(fileName = "导出.xls")
public class ExportPersonExcelModal implements Serializable {
    @Excel(name = "姓名")
    private String name;
    @Excel(name = "性别", dict = DICT_XB)
    private String sex;
    @Excel(name = "民族", dict = DICT_MZ)
    private String mz;
}

Use the exportExcel method of the tool class EasyPoiUtil to realize the excel export function, and only need to pass two parameters.

  • Excel data list

  • HttpServletResponse output stream

EasyPoiUtil.exportExcel(list, response);

Compare EasyPoi and EasyPoiX

Compared with EasyPoi's official export method, EasyPoiX is more convenient to handle the dictionary when exporting, and does not need to provide additional

Implementation of IExcelDictHandler, the two implementation methods of EasyPoi's IExcelDictHandler interface are as follows:

/**
 * 从值翻译到名称
 * @param dict  字典 Key
 * @param obj   对象
 * @param name  属性名称
 * @param value 属性值
 * @return
 */
public String toName(String dict, Object obj, String name, Object value);

/**
 * 从名称翻译到值
 * @param dict  字典 Key
 * @param obj   对象
 * @param name  属性名称
 * @param value 属性值
 * @return
 */
public String toValue(String dict, Object obj, String name, Object value);

The toName method is used to convert dictionary values ​​to dictionary names when exporting; the toValue method is used to convert dictionary names to dictionary values ​​when importing. If you use EasyPoi, multiple if-else judgments are required when multiple dictionaries share the same IExcelDictHandler interface, and there will be efficiency problems when repeatedly querying the same dictionary during Excel import and export. In addition, the query interfaces of multiple dictionaries in the project are often The same method, so it seems that the code is repeated and not beautiful.

In order to solve the above problems, EasyPoiX provides a default dictionary conversion interface for dictionary conversion when exporting, and there is no need to pass parameters in the tool class to specify (if there are special requirements, you can also specify a custom dictionary conversion interface). So the question is, where is the value method of the dictionary defined? EasyPoiX provides a @ExcelDictDataType annotation and an ExcelDictDataService interface, the code example is as follows:

@ExcelDictDataType({DICT_XB, DICT_MZ, DICT_COMMUNITY})
@Component
public class BasicDictDataService implements ExcelDictDataService<String, String> {
    public static final String DICT_XB = "XB";
    public static final String DICT_MZ = "MZ";

    @Override
    public Map<String, String> getData(DataParam params) {
        // 获取字典值的实现代码
        // 不论是字典的名称转值还是值转名称,都会从这里取值
        // 返回的 Map 类型,key 是字典值,value 是字典名称
        return map;
    }
}
  • The IExcelDictHandler interface, the parameters to be displayed in the export method; the ExcelDictDataService interface does not need to display the parameters, because the @ExcelDictDataType annotation has marked that the current class is the implementation class of the dictionary value, and the key of the dictionary is configured in the value() attribute of the annotation , multiple dictionary keys can be provided, so the ExcelDictDataService interface can satisfy multiple dictionary values.

  • The IExcelDictHandler interface needs two forward and reverse conversion methods; the ExcelDictDataService interface only needs to implement a dictionary value method.

  • The IExcelDictHandler interface needs to add multiple if-else in the implementation method, and frequently calling the toName and toValue methods during Excel import and export will cause efficiency problems; Query in the cache, if there is no cache, the real query call will be made. After a request ends, these caches are cleared to avoid running out of memory.

Big data export to Excel

Implementation steps

Use the exportBigExcel method of the tool class EasyPoiUtil to realize the excel big data export function, pass parameters:

  • Entity model Class

  • IExcelExportServer interface. You can use the default interface to implement the class DefaultBigExcelExportServer, and the constructor needs to provide a lamda expression for pagination query

  • The condition parameter of pagination query, Object type

  • HttpServletResponse output stream

DefaultBigExcelExportServer ser = new DefaultBigExcelExportServer((queryParams, page) -> findPage(queryParams, page));
EasyPoiUtil.exportBigExcel(ExportPersonExcelModal.class, ser, param, response);

Compare EasyPoi and EasyPoiX

EasyPoi needs to implement the IExcelExportServer interface, and EasyPoiX does not need to implement it, but passes a DefaultBigExcelExportServer object, and provides a lamda expression for pagination query when constructing the object

Or called pagination query method. The advantage of this is that there is no need to create a separate IExcelExportServer interface implementation class for each Excel.

/**
 * 导出数据接口
 */
public interface IExcelExportServer {
    /**
     * 查询数据接口
     * @param queryParams 查询条件
     * @param page        当前页数从 1 开始
     * @return
     */
    public List<Object> selectListForExcelExport(Object queryParams, int page);
}

Import Excel

The importing Excel scene is more complicated than the exporting scene. In addition to calling the import method of the tool class and realizing the value conversion of the dictionary, two additional scenes are added: 1. Data verification; 2. Data processing. Neither of these scenarios is a mandatory step when importing, but both are common. These two scenarios are described in detail below.

Data validation

EasyPoi provides a data verification interface IExcelVerifyHandler

/**
 * 导入校验接口
 * @author JueYue
 *  2014 年 6 月 23 日 下午 11:08:21
 */
public interface IExcelVerifyHandler<T> {
    /**
     * 导入校验方法
     * @param obj
     *            当前对象
     * @return
     */
    public ExcelVerifyHandlerResult verifyHandler(T obj);
}

EasyPoiX has made a small optimization to it, and added the verification function of data duplication. For example, when importing a personnel list, if the same ID number appears, it will prompt that the ID number that appears later is duplicated. The method of use is also very simple, just need to inherit a DefaultExcelVerifyHandler class, the sample code is as follows:

@Component
public class ImportPersonExcelVerifyHandler extends DefaultExcelVerifyHandler<ImportPersonExcelModal> {
    @Override
    public ExcelVerifyHandlerResult verifyHandler(ImportPersonExcelModal obj) {
        Set<Object> idCards = getRepeatTmp("idCard");
        if (idCards.contains(obj.getIdCard())) {
            return new ExcelVerifyHandlerResult(false, "证件号重复");
        }
        idCards.add(obj.getIdCard());
        return new ExcelVerifyHandlerResult(true);
    }
}

It should be pointed out here that if the returned ExcelVerifyHandlerResult type is constructed, if the success parameter passes true, it means that the verification is passed; if false is passed, the errorMsg will be set to the verification in the element that failed the verification in the imported data returned by the import method. error message. So the imported entity class needs to inherit the ImportExcelModel class.

public class ImportExcelModel implements Serializable, IExcelDataModel, IExcelModel {
    /**
     * 行号
     */
    private int rowNum;
    /**
     * 错误消息
     */
    @Excel(name = "错误信息")
    private String errorMsg;

    @Override
    public int getRowNum() {
        return rowNum;
    }
    @Override
    public void setRowNum(int rowNum) {
        this.rowNum = rowNum;
    }
    @Override
    public String getErrorMsg() {
        return errorMsg;
    }
    @Override
    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

data processing

The data processing scenario is actually very similar to the data dictionary scenario , both of which require some conversion operations on the excel data. But their difference is that the data dictionary is more inclined to convert globally unique constants, so the dict attribute in the @Excel annotation is shared among different Excel entities; and the data processing is performed after the data dictionary conversion operation is completed, because Data processing fields may use other fields in the current Excel row during conversion, including converted dictionary values.

For example, if there is a linkage query relationship between multiple fields in the imported excel row data, custom data processing is required. For example, there are districts and buildings in a row of imported fields. The conversion of district names and district ids does not need to rely on other fields, but the conversion of building names and building ids needs to be queried according to the district id, although it can also be converted through a dictionary , but it is better to convert the data processing step after the dictionary conversion is safer.

In addition, dictionary conversion cannot use different conversion logic for the same keyword in different entities, because the meaning of the dictionary itself is globally unique. The data processing transformation can achieve the same keyword in different entities using different transformations. The following uses the two code implementations of EasyPoi and EasyPoiX to introduce their differences in more detail.

Data processing of EasyPoi

EasyPoi provides default data handler virtual class ExcelDataHandlerDefaultImpl

public abstract class ExcelDataHandlerDefaultImpl<T> implements IExcelDataHandler<T> {
    /**
     * 需要处理的字段
     */
    private String[] needHandlerFields;

    // 省略部分代码.....

    @Override
    public Object importHandler(T obj, String name, Object value) {
        return value;
    }
    @Override
    public Object exportHandler(T obj, String name, Object value) {
        return value;
    }
    @Override
    public void setNeedHandlerFields(String[] needHandlerFields) {
        this.needHandlerFields = needHandlerFields;
    }
}
  • The setNeedHandlerFields method is used to specify which fields need to be processed. The parameter is a string array, and each element corresponds to the name attribute of the @Excel annotation in the Excel entity, that is, the header name of Excel

  • The importHandler interface can be rewritten to the conversion logic we need, and the default is to return the original value, that is, the field is not converted

  • The exportHandler interface can be rewritten to the conversion logic we need. The default is to return the original value, that is, not to convert the field

Now, data processing can be realized by inheriting the ExcelDataHandlerDefaultImpl class

@Component
public class PersonExcelDataHandler extends ExcelDataHandlerDefaultImpl<ImportPersonExcelModal> {
    @PostConstruct
    public void init() {
        setNeedHandlerFields(new String[]{"小区", "楼栋"});
    }
    @Override
    public Object importHandler(ImportPersonExcelModal obj, String name, Object value) {
        if(Objects.isNull(value)){
            return value;
        }
        if(name.equals("楼栋")){
            //根据 obj.getCommunityId() 和 obj.getBuildingName(),查询楼栋 id
			return 楼栋 id;
        }
        return value;
    }
    @Override
    public Object exportHandler(ImportPersonExcelModal obj, String name, Object value) {
        if(Objects.isNull(value)){
            return value;
        }
        if(name.equals("楼栋")){
            //根据 obj.getCommunityId() 和 obj.getBuildingId(),查询楼栋名称
			return 楼栋名称;
        }
        return value;
    }
}

Data processing in EasyPoiX

EasyPoiX encapsulates a SimpleExcelDataHandler virtual class, inheriting this virtual class can avoid the trouble of manually calling the setNeedHandlerFields method.

In addition, SimpleExcelDataHandler provides the implementation of the importHandler and exportHandler interfaces by default. Just like the dictionary conversion, it only needs to provide an implementation of the getData interface to provide the value logic of the data to be processed.

@Component
@ExcelHandleDataType(clazz = ImportPersonExcelModal.class, value = {BUILDING_NAME, UNIT_NAME})
public class PersonExcelDataHandler extends SimpleExcelDataHandler<ImportPersonExcelModal> {
    public static final String BUILDING_NAME = "楼栋";
    public static final String UNIT_NAME = "单元";
    
    @Override
    public Map<String, String> getData(DataParam params) {
        switch (params.getDict()) {
            case BUILDING_NAME:
                return getBuildingData(params);
            case UNIT_NAME:
                return getUnitData(params);
            default:
                return null;
        }
    }

    private Map<String, String> getBuildingData(DataParam params) {
        Object row = params.getRow();
        String communityId = BeanUtil.getProperty(row, "communityId");
        //返回楼栋键值对,key 是楼栋 id,value 是楼栋名称
    }

    private Map<String, String> getUnitData(DataParam params) {
        Object row = params.getRow();
        String buildingId = BeanUtil.getProperty(row, "buildingId");
        //返回单元键值对,key 是单元 id,value 是单元名称
    }

}

DataParam.java

@Data
@Accessors(chain = true)
public class DataParam {
    private String dict;
    private String key;
    private Object row;
}

If the actual business does not need the default data processing implementation, you can also rewrite the importHandler and exportHandler interfaces after inheriting the SimpleExcelDataHandler virtual class, just like the data processing method of EasyPoi, which is more flexible.

Summarize

EasyPoiX is a non-invasive secondary package for EasyPoi in order to quickly import and export Excel. At the same time, it uses the principle of spring-boot-starter, so that it can reuse EasyPoiX in the form of a jar module in the spring-boot project function. If any friends encounter other scenarios in actual use, you can also contact me.

My name is IT Guoguo , my contact information is below, thank you!

Please contact me

The above scenarios have corresponding test code examples. If you want to learn more, please visit the address:

https://gitee.com/chenzhaoplus/easypoix-spring-boot-starter

https://github.com/chenzhaoplus/easypoix-spring-boot-starter

https://blog.csdn.net/cz285933169?spm=1010.2135.3001.5421

Guess you like

Origin blog.csdn.net/cz285933169/article/details/128785856