Java使用poi做加自定义注解实现对象与Excel相互转换

引入依赖

maven

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>

Gradle

implementation group: 'org.apache.poi', name: 'poi', version: '3.17'

代码展示

1、自定义注解类

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface Excel {
    String name();//列的名字

    int width() default 6000;//列的宽度

    int index() default -1;//决定生成的顺序

    boolean isMust() default true; // 是否为必须值,默认是必须的
}

2、Java的Excel对象,只展现了field,get与set方法就忽略了

public class GoodsExcelModel {
    @Excel(name = "ID_禁止改动", index = 0, width = 0)
    private Long picId;//picId
    @Excel(name = "产品ID_禁止改动", index = 1, width = 0)
    private Long productId;
    @Excel(name = "型号", index = 3)
    private String productName;//产品型号
    @Excel(name = "系列", index = 2)
    private String seriesName;//系列名字
    @Excel(name = "库存", index = 5)
    private Long quantity;
    @Excel(name = "属性值", index = 4)
    private String propValue;
    @Excel(name = "价格", index = 6)
    private Double price;
    @Excel(name = "商品编码", index = 7, isMust = false)
    private String outerId;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long dbId; // 数据库自增长id

    private Date createTime; // 记录创建时间
 }

3、Excel表格与对象转换的工具类,使用时指定泛型参数和泛型的class即可

public class ExcelUtil {
    private static final String GET = "get";
    private static final String SET = "set";
    private static Logger logger = LoggerFactory.getLogger(ExcelUtil.class);

    /**
     * 将对象转换成Excel
     *
     * @param objList 需要转换的对象
     * @return 返回是poi中的对象
     */
    public static HSSFWorkbook toExcel(List objList) {
        if (CollectionUtils.isEmpty(objList)) throw new NullPointerException("无效的数据");
        Class aClass = objList.get(0).getClass();
        Field[] fields = aClass.getDeclaredFields();
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet();
        for (int i = 0; i < objList.size(); i++) {
            HSSFRow row = sheet.createRow(i + 1);//要从第二行开始写
            HSSFRow topRow = null;
            if (i == 0) topRow = sheet.createRow(0);
            for (Field field : fields) {
                Excel excel = field.getAnnotation(Excel.class);//得到字段是否使用了Excel注解
                if (excel == null) continue;
                HSSFCell cell = row.createCell(excel.index());//设置当前值放到第几列
                String startName = field.getName().substring(0, 1);
                String endName = field.getName().substring(1, field.getName().length());
                String methodName = new StringBuffer(GET).append(startName.toUpperCase()).append(endName).toString();
                try {
                    Method method = aClass.getMethod(methodName);//根据方法名获取方法,用于调用
                    Object invoke = method.invoke(objList.get(i));
                    if (invoke == null) continue;
                    cell.setCellValue(invoke.toString());
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                if (topRow == null) continue;
                HSSFCell topRowCell = topRow.createCell(excel.index());
                topRowCell.setCellValue(excel.name());
                sheet.setColumnWidth(excel.index(), excel.width());
            }
        }

        return workbook;
    }

    /**
     * 将Excel文件转换为指定对象
     *
     * @param file 传入的Excel
     * @param c    需要被指定的class
     * @return
     * @throws IOException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> List<T> excelFileToObject(MultipartFile file, Class<T> c) throws IOException, IllegalAccessException, InstantiationException {
        //key为反射得到的下标,value为对于的set方法
        Map<Integer, String> methodMap = new HashMap<>();
        //保存第一列的值与对应的下标,用于验证用户是否删除了该列,key为下标,value为名字
        Map<Integer, String> startRowNameMap = new HashMap<>();
        //用来记录当前参数是否为必须的
        Map<Integer, Boolean> fieldIsMustMap = new HashMap<>();
        //得到所有的字段
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            Excel excel = field.getAnnotation(Excel.class);
            if (excel == null) continue;
            String startName = field.getName().substring(0, 1);
            String endName = field.getName().substring(1, field.getName().length());
            String methodName = new StringBuffer(SET).append(startName.toUpperCase()).append(endName).toString();
            methodMap.put(excel.index(), methodName);
            startRowNameMap.put(excel.index(), excel.name());
            fieldIsMustMap.put(excel.index(), excel.isMust());
        }

        String fileName = file.getOriginalFilename();
        Workbook wb = fileName.endsWith(".xlsx") ? new XSSFWorkbook(file.getInputStream()) : new HSSFWorkbook(file.getInputStream());
        Sheet sheet = wb.getSheetAt(0);
        Row sheetRow = sheet.getRow(0);
        for (Cell cell : sheetRow) {
            Integer columnIndex = cell.getColumnIndex();
            if (cell.getCellTypeEnum() != CellType.STRING) throw new ExcelException("excel校验失败, 请勿删除文件中第一行数据 !!!");
            String value = cell.getStringCellValue();
            String name = startRowNameMap.get(columnIndex);
            if (name == null) throw new ExcelException("excel校验失败,请勿移动文件中任何列的顺序!!!");
            if (!name.equals(value)) throw new ExcelException("excel校验失败,【" + name + "】列被删除,请勿删除文件中任何列 !!!");
        }
        sheet.removeRow(sheetRow);//第一行是不需要被反射赋值的
        List<T> models = new ArrayList<>();
        for (Row row : sheet) {
            if (row == null || !checkRow(row)) continue;
            T obj = c.newInstance();//创建新的实例化对象
            Class excelModelClass = obj.getClass();
            startRowNameMap.entrySet().forEach(x -> {
                Integer index = x.getKey();
                Cell cell = row.getCell(index);
                String methodName = methodMap.get(index);
                if (StringUtils.isEmpty(methodName)) return;
                List<Method> methods = Lists.newArrayList(excelModelClass.getMethods()).stream()
                        .filter(m -> m.getName().startsWith(SET)).collect(Collectors.toList());
                String rowName = startRowNameMap.get(index);//列的名字
                for (Method method : methods) {
                    if (!method.getName().startsWith(methodName)) continue;
                    //检测value属性
                    String value = valueCheck(cell, rowName, fieldIsMustMap.get(index));
                    //开始进行调用方法反射赋值
                    methodInvokeHandler(obj, method, value);
                }
            });
            models.add(obj);
        }
        return models;
    }

    /**
     * 检测当前需要赋值的value
     *
     * @param cell    当前循环行中的列对象
     * @param rowName 列的名字{@link Excel}中的name
     * @param isMust  是否为必须的
     * @return 值
     */
    private static String valueCheck(Cell cell, String rowName, Boolean isMust) {
        //有时候删除单个数据会造成cell为空,也可能是value为空
        if (cell == null && isMust) {
            throw new ExcelException("excel校验失败,【" + rowName + "】中的数据禁止单个删除");
        }
        if (cell == null) return null;
        cell.setCellType(CellType.STRING);
        String value = cell.getStringCellValue();
        if ((value == null || value.trim().isEmpty()) && isMust) {
            throw new ExcelException("excel校验失败,【" + rowName + "】中的数据禁止单个删除");
        }
        return value;
    }

    /**
     * 反射赋值的处理的方法
     *
     * @param obj      循环创建的需要赋值的对象
     * @param method 当前对象期中一个set方法
     * @param value  要被赋值的内容
     */
    private static void methodInvokeHandler(Object obj, Method method, String value) {
        Class<?> parameterType = method.getParameterTypes()[0];
        try {
            if (parameterType == null) {
                method.invoke(obj);
                return;
            }
            String name = parameterType.getName();
            if (name.equals(String.class.getName())) {
                method.invoke(obj, value);
                return;
            }
            if (name.equals(Long.class.getName())) {
                method.invoke(obj, Long.valueOf(value));
                return;
            }
            if (name.equals(Double.class.getName())) {
                method.invoke(obj, Double.valueOf(value));
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static boolean checkRow(Row row) {
        try {
            if (row == null) return false;
            short firstCellNum = row.getFirstCellNum();
            short lastCellNum = row.getLastCellNum();
            if (firstCellNum < 0 && lastCellNum < 0) return false;
            if (firstCellNum != 0) {
                for (short i = firstCellNum; i < lastCellNum; i++) {
                    Cell cell = row.getCell(i);
                    String cellValue = cell.getStringCellValue();
                    if (!StringUtils.isBlank(cellValue)) return true;
                }
                return false;
            }
            return true;
        } catch (Exception e) {
            return true;
        }

    }

4、导出Excel与导入Excel的示例代码

使用展示

1、选择数据

2、设置基本数据,然后导出表格

3、导出表格效果,在图片中看到A和B列没有显示出来,这是因为我将其宽度配置为了0

4、将必须参数删除后上传测试,如下图中,商品编码我设置isMust为false所以删除数据就不会出现此问题。会提示验证失败,具体错误查看图片

5、将列中值的顺序调整测试,也会提示验证失败,具体效果如下图

6、正常上传测试,具体效果下如图

到此这篇关于Java使用poi做加自定义注解实现对象与Excel相互转换的文章就介绍到这了,更多相关Java 对象与Excel相互转换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java通过反射将 Excel 解析成对象集合实例

    1.这是一个通过Java反射机制解析的工具类 2.使用时只需创建对应的对象,并在Excel的第一行填上对应的属性名 3.首先要添加相关的jar包: poi-3.8.jar poi-ooxml-3.9.jar poi-ooxml-schemas-3.9.jar xmlbeans-2.6.0.jar 4.看一下Excel的内容: 5.创建对应的实体类: package com.office.user.dto; public class UserDTO { private String idUser;

  • java实现把对象数组通过excel方式导出的功能

    一.导入相关jar包,pom依赖如下: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>RELEASE</version> </dependency> 二.开始撸代码 1.如果导出功能使用的比较多,可以将其做成一个工具类,对我下面贴出的代码进行改造 //结果返回的是写入的记录数(以下用的是自

  • Java使用excel工具类导出对象功能示例

    本文实例讲述了Java使用excel工具类导出对象功能.分享给大家供大家参考,具体如下: package com.gcloud.common; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.streaming.SXSSFSheet; import

  • Java使用poi做加自定义注解实现对象与Excel相互转换

    引入依赖 maven <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> Gradle implementation group: 'org.apache.poi', name: 'poi', version: '3.17' 代码展示 1

  • Java 使用POI生成带联动下拉框的excel表格实例代码

    废话不多说了,直接给大家贴代码了,具体代码如下所示: import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.poi.hssf.

  • Java Validation Api如何实现自定义注解

    背景 官方提供的注解多数可以解决现实业务场景中基本业务校验,但有些特殊场景因业务的复杂性,也还是需要在入口处对入参进行各种角度的校验,以求简化业务层的处理,降低业务处理复杂性与方法入口的强约束性. 以上背景,下面就举个简单Demo进行自定义注解校验的实现. 注解定义类 import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; impor

  • Java自定义注解用法实例小结

    本文实例讲述了Java自定义注解用法.分享给大家供大家参考,具体如下: 一 自定义注解语法 [public] @interface Annotation的名称 { [数据类型 变量名称();] } 要自定义注解,需要使用@interface方式进行定义,在定义注解时也可以定义各种变量,但是变量之后必须使用括号(). 提示:使用@interface就相对于继承了Annotation接口.在程序中使用@interface声明Annotation,那么此Annotation实际相对于继承了Annota

  • Java注解Annotation与自定义注解详解

    一:Java注解简介 开发中经常使用到注解,在项目中也偶尔会见到过自定义注解,今天就来探讨一下这个注解是什么鬼,以及注解的应用场景和如何自定义注解. 下面列举开发中常见的注解 @Override:用于标识该方法继承自超类, 当父类的方法被删除或修改了,编译器会提示错误信息(我们最经常看到的toString()方法上总能看到这货) @Deprecated:表示该类或者该方法已经不推荐使用,已经过期了,如果用户还是要使用,会生成编译的警告 @SuppressWarnings:用于忽略的编译器警告信息

  • Java 自定义注解的魅力

    注解是什么? ①.引用自维基百科的内容: Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法 元数据 . Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反射获取标注内容.在编译器生成类文件时,标注可以被嵌入到字节码中.Java虚拟机可以保留标注内容,在运行时可以获取到标注内容. 当然它也支持自定义Java标注. ②.引用自网络的内容: Java 注解是在 JDK5 时引入的新特性,注解(也被称为 元数据 )为我们在代码

  • javaSE基础java自定义注解原理分析

    目录 1. 从注释的角度来理解注解 2.提出问题 3.编写注解 4.通过Java反射获取方法的注解信息 结束 注解在JavaSE中算是比较高级的一种用法了,为什么要学习注解,我想大概有以下几个原因: 1. 可以更深层次地学习Java,理解Java的思想. 2. 有了注解的基础,能够方便阅读各种框架的源码,比如hibernate,SpringMVC等等.里面就用到了大量的注解.即便无法阅读源码,以后使用这些框架,会有一种心理上的安全感. 3. 方便今后跟别人吹牛.(当然,这也很重要.) 好了,话不

  • 使用自定义注解实现加解密及脱敏方式

    目录 自定义注解实现加解密及脱敏 定义自定义注解 构造AOP逻辑 测试 脱敏逻辑 自定义一种字符串的加密与解密 自定义注解实现加解密及脱敏 定义自定义注解 @Documented @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Order(Ordered.HIGHEST_PRECEDENCE) public @interface PrivateData { } @Documented @Target({Eleme

  • 使用webservice自定义注解处理参数加解密问题

    目录 webservice自定义注解处理参数加解密 代码实现 webservice注解汇总 @WebService @WebMethod @Oneway @WebParam @WebResult @HandlerChain webservice自定义注解处理参数加解密 前一段项目中用到了webservice,正好自己之前也了解过一点apache的cxf框架,所以就采用了cxf来实现webservice服务端,本身实现并没技术难点,但是项目为了保证安全性,采用了传输加密的过程,所以大部分请求参数需

  • Java自定义注解实现Redis自动缓存的方法

    在实际开发中,可能经常会有这样的需要:从MySQL中查询一条数据(比如用户信息),此时需要将用户信息保存至Redis. 刚开始我们可能会在查询的业务逻辑之后再写一段Redis相关操作的代码,时间长了后发现这部分代码实际上仅仅做了Redis的写入动作,跟业务逻辑没有实质的联系,那么有没有什么方法能让我们省略这些重复劳动呢? 首先想到用AOP,在查询到某些数据这一切入点(Pointcut)完成我们的切面相关处理(也就是写入Redis).那么,如何知道什么地方需要进行缓存呢,也就是什么地方需要用到AO

随机推荐