若依POI(Excel)模块探究

Posted 卷王2048

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了若依POI(Excel)模块探究相关的知识,希望对你有一定的参考价值。

若依POI(Excel)模块探究

POI是一个java操作Excel的工具包

POI中的对象

工作薄对象WorkBook

WorkBook对象的实现类有3个:

HSSFWorkbook(03版Excel,最多只有65536行,导入导出速度快)

XSSFWorkbook(07版Excel,支持百万数据的导入导出,导入导出速度慢)

SXSSFWorkbook(07版Excel升级版,会在过程中生成中间文件,导入导出速度快)

工作表对象Sheet

行对象Row

单元格对象Cell

最基础的demo

需要的maven依赖

<dependencies>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.2</version>
  </dependency>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.2</version>
  </dependency>
  <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
  </dependency>
</dependencies>

导出demo

@Test
public void SimplePIO() throws IOException 

  String path = "E:\\\\java代码\\\\POI练习代码\\\\output\\\\";
  //新建工作簿
  Workbook workbook = new SXSSFWorkbook();
  //用工作簿来新建工作表
  Sheet sheet = workbook.createSheet(String.valueOf(0));
  //用工作表新建行
  Row row = sheet.createRow(0);
  //用行新建单元格
  Cell cell = row.createCell(0);
  //向单元格中写入值
  cell.setCellValue("666");


  Row row1 = sheet.createRow(1);
  //向第二行的100列添加元素
  for (int i = 0; i < 100; i++) 
    row1.createCell(i).setCellValue(i);
  

  //用输出流创建一个文件
  OutputStream out = new FileOutputStream(path+"test.xlsx");
  //将工作簿的内容写在Excel中,输出
  workbook.write(out);

  out.close();

导入demo


将实体类转化为Excel

java中的注解类

想自定义注解类,先要了解JDK自带的注解

@Target 注解

功能:指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。

ElementType的取值包含以下几种:

  • TYPE:类,接口或者枚举
  • FIELD:域,包含枚举常量
  • METHOD:方法
  • PARAMETER:参数
  • CONSTRUCTOR:构造方法
  • LOCAL_VARIABLE:局部变量
  • ANNOTATION_TYPE:注解类型
  • PACKAGE:包

@Retention 注解

功能:指明修饰的注解的生存周期,即会保留到哪个阶段。

RetentionPolicy的取值包含以下三种:

  • SOURCE:源码级别保留,编译后即丢弃。
  • CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
  • RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。

@Documented 注解

功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。

@Inherited注解

功能:允许子类继承父类中的注解。

注意!:

@interface意思是声明一个注解,方法名对应参数名,返回值类型对应参数类型。

在若依中,将实体类转化为Excel的思路:

1.编写一个注解类Excel

思路:

通过注解类中的函数和内部类,就可以通过注解中的参数,向注解类中传值;注解类中的函数和内部类,在使用时都是用来传参数

package com.yutao.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.math.BigDecimal;
import com.yutao.common.utils.poi.ExcelHandlerAdapter;

/**
 * 自定义导出Excel数据注解
 * 
 * @author yutao
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel

    /**
     * 导出时在excel中排序
     */
    public int sort() default Integer.MAX_VALUE;

    /**
     * 导出到Excel中的名字.
     */
    public String name() default "";

    /**
     * 日期格式, 如: yyyy-MM-dd
     */
    public String dateFormat() default "";

    /**
     * 如果是字典类型,请设置字典的type值 (如: sys_user_sex)
     */
    public String dictType() default "";

    /**
     * 读取内容转表达式 (如: 0=男,1=女,2=未知)
     */
    public String readConverterExp() default "";

    /**
     * 分隔符,读取字符串组内容
     */
    public String separator() default ",";

    /**
     * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
     */
    public int scale() default -1;

    /**
     * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
     */
    public int roundingMode() default BigDecimal.ROUND_HALF_EVEN;

    /**
     * 导出类型(0数字 1字符串)
     */
    public ColumnType cellType() default ColumnType.STRING;

    /**
     * 导出时在excel中每个列的高度 单位为字符
     */
    public double height() default 14;

    /**
     * 导出时在excel中每个列的宽 单位为字符
     */
    public double width() default 16;

    /**
     * 文字后缀,如% 90 变成90%
     */
    public String suffix() default "";

    /**
     * 当值为空时,字段的默认值
     */
    public String defaultValue() default "";

    /**
     * 提示信息
     */
    public String prompt() default "";

    /**
     * 设置只能选择不能输入的列内容.
     */
    public String[] combo() default ;

    /**
     * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
     */
    public boolean isExport() default true;

    /**
     * 另一个类中的属性名称,支持多级获取,以小数点隔开
     */
    public String targetAttr() default "";

    /**
     * 是否自动统计数据,在最后追加一行统计数据总和
     */
    public boolean isStatistics() default false;

    /**
     * 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)
     */
    public Align align() default Align.AUTO;

    /**
     * 自定义数据处理器
     */
    public Class<?> handler() default ExcelHandlerAdapter.class;

    /**
     * 自定义数据处理器参数
     */
    public String[] args() default ;

    public enum Align
    
        AUTO(0), LEFT(1), CENTER(2), RIGHT(3);
        private final int value;

        Align(int value)
        
            this.value = value;
        

        public int value()
        
            return this.value;
        
    

    /**
     * 字段类型(0:导出导入;1:仅导出;2:仅导入)
     */
    Type type() default Type.ALL;

    public enum Type
    
        ALL(0), EXPORT(1), IMPORT(2);
        private final int value;

        Type(int value)
        
            this.value = value;
        

        public int value()
        
            return this.value;
        
    

    public enum ColumnType
    
        NUMERIC(0), STRING(1), IMAGE(2);
        private final int value;

        ColumnType(int value)
        
            this.value = value;
        

        public int value()
        
            return this.value;
        
    

2.编写注解工具类ExcelUtils

整体思路与模块划分:

1.类的成员变量,即Excel文件对象中的属性

/**
     * Excel sheet最大行数,默认65536
     */
    public static final int sheetSize = 65536;

    /**
     * 工作表名称
     */
    private String sheetName;

    /**
     * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
     */
    private Excel.Type type;

    /**
     * 工作薄对象
     */
    private Workbook wb;

    /**
     * 工作表对象
     */
    private Sheet sheet;

    /**
     * 样式列表
     */
    private Map<String, CellStyle> styles;

    /**
     * 导入导出数据列表
     */
    private List<T> list;

    /**
     * 注解列表
     */
    private List<Object[]> fields;

    /**
     * 当前行号
     */
    private int rownum;
    
    /**
     * 标题
     */
    private String title;

    /**
     * 最大高度
     */
    private short maxHeight;

    /**
     * 统计列表
     */
    private Map<Integer, Double> statistics = new HashMap<Integer, Double>();

    /**
     * 数字格式
     */
    private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");

    /**
     * 实体对象
     */
    public Class<T> clazz;

2.操作注解,从成员变量上的自定义注解中获取信息

操作的关键就是反射类中的几个方法

java中注解相关的操作

java注解-最通俗易懂的讲解

总结来说,有以下几个常用方法:

getDeclaredFields()返回该类中所有成员变量的Field
getDeclaredAnnotations()返回该元素上方所有注解的数组
getDeclaredMethods()返回该类中所有成员方法的Field
isAnnotationPresent()如果此元素上有指定类型的注释,则返回true,否则返回false。该方法主要用于方便地访问标记注释。 这个方法返回的真值相当于:getAnnotation(annotationClass) != null
    /**
     * 获取字段注解信息
     */
    public List<Object[]> getFields()
    
        List<Object[]> fields = new ArrayList<Object[]>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        for (Field field : tempFields)
        
            // 单注解
            if (field.isAnnotationPresent(Excel.class))
            
                Excel attr = field.getAnnotation(Excel.class);
                if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type))
                
                    field.setAccessible(true);
                    fields.add(new Object[]  field, attr );
                
            

            // 多注解
            if (field.isAnnotationPresent(Excels.class))
            
                Excels attrs = field.getAnnotation(Excels.class);
                Excel[] excels = attrs.value();
                for (Excel attr : excels)
                
                    if (attr != null && (attr.type() == Excel.Type.ALL || attr.type() == type))
                    
                        field.setAccessible(true);
                        fields.add(new Object[]  field, attr );
                    
                
            
        
        return fields;
    

3.导出模块

导出的关键就是如何填写每行,每个单元格的数据,在填写的过程中,会调用到其他规定格式的函数

解析实体对象中的每个属性值

    /**
     * 以类的属性的get方法方法形式获取值
     * 
     * @param o
     * @param name
     * @return value
     * @throws Exception
     */
    private Object getValue(Object o, String name) throws Exception
    
        if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name))
        
            Class<?> clazz = o.getClass();
            Field field = clazz.getDeclaredField(name);
            field.setAccessible(true);
            o = field.get(o);
        
        return o;
    
    /**
     * 获取bean中的属性值
     * 
     * @param vo 实体对象
     * @param field 字段
     * @param excel 注解
     * @return 最终的属性值
     * @throws Exception
     */
    private Object getTargetValue(T vo, Field field, Excel excel) throws Exception
    
        Object o = field.get(vo);
        if (StringUtils.isNotEmpty(excel.targetAttr()))
        
            String target = excel.targetAttr();
            if (target.contains("."))
            
                String[] targets = target.split("[.]");
                for (String name : targets)
                
                    o = getValue(o, name);
                
            
            else
            
                o = getValue(o, target);
            
        
        return o;
    

将单个实体类中每个属性字段,填入每行的对应单元格中

    public Cell addCell(Excel attr, Row row, T vo, Field field, int column)
    
        Cell cell = null;
        try
        
            // 设置行高
            row.setHeight(maxHeight);
            // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
            if (attr.isExport())
            
                // 创建cell
                cell = row.createCell(column);
                int align = attr.align().value();
                cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));

                // 用于读取对象中的属性
                Object value = getTargetValue(vo, field, attr);
                String dateFormat = attr.dateFormat();
                String readConverterExp = attr.readConverterExp();
                String separator = attr.separator();
                String dictType = attr.dictType();
                if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value))
                
                    cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value));
                
                else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value))
                
                    cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator以上是关于若依POI(Excel)模块探究的主要内容,如果未能解决你的问题,请参考以下文章

若依POI(Excel)模块探究

若依权限模块探究

若依权限模块探究

若依权限模块探究

若依登陆模块探究

若依登陆模块探究