【Java】EasyExecl数据导入

news2024/12/23 10:41:53

【Java】EasyExecl数据导入

📔 千寻简笔记介绍

千寻简笔记已开源,Gitee与GitHub搜索chihiro-notes,包含笔记源文件.md,以及PDF版本方便阅读,且是用了精美主题,阅读体验更佳,如果文章对你有帮助请帮我点一个Star

文章目录

  • 【Java】EasyExecl数据导入
    • 📔 千寻简笔记介绍
    • EasyExcel介绍
    • 为什么使用EasyExcel
    • 实现思路
    • 实现代码
      • **ImprotListener
      • ImportController
      • service
      • ImportDemo
      • ImprotExcelVo
      • DateConverter
      • EasyExcelUtils
      • BeanUtils

EasyExcel介绍

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

为什么使用EasyExcel

在这里插入图片描述

EasyExcel 是一个基于 Java 的简单、高效的 Excel 文件读写框架。它提供了易于使用的 API,可以快速读取和写入 Excel 文件,支持大数据量的处理,并且具有良好的性能。

  1. 简单易用:EasyExcel 提供了简洁而直观的 API,使得读写 Excel 文件变得简单易用,无需繁琐的操作和复杂的配置。
  2. 高性能:EasyExcel 使用了流式读写的方式,能够高效地处理大规模的 Excel 文件,提供了优秀的性能和内存管理。
  3. 支持多种数据源:EasyExcel 可以与多种数据源进行集成,包括 Java 对象、数据库查询结果、XML、JSON 等,方便灵活地读写各种数据格式。
  4. 大数据量支持:EasyExcel 对于大规模数据的读写具有优势,能够快速处理大量的数据,同时提供了灵活的数据导入导出选项。
  5. 导入导出功能:EasyExcel 提供了丰富的导入和导出功能,可以方便地读取和写入 Excel 文件,支持自定义转换器、监听器等扩展功能。
  6. 跨平台兼容:EasyExcel 可以在 Windows、Linux、Mac 等多个操作系统上运行,并且与 Java 应用程序完美集成,具有良好的跨平台兼容性。

总的来说,EasyExcel 提供了简单、高效、灵活的 Excel 文件读写解决方案,使开发者能够轻松处理 Excel 数据,节省开发时间和资源,提高工作效率。无论是数据导入、导出、数据分析还是报表生成,EasyExcel 都是一个强大的选择。

实现思路

  1. 引入 EasyExcel 依赖:在项目的 Maven 配置文件中,添加 EasyExcel 的依赖项。
  2. 创建 Excel 写入器:通过 EasyExcel.write() 方法创建一个 Excel 写入器,指定要生成的 Excel 文件路径或输出流。
  3. 定义 Excel 数据模型:创建一个 Java 类,用于表示 Excel 中的数据行,通过在类上添加注解,指定 Excel 列和数据的映射关系。
  4. 写入 Excel 数据:通过 write(List<? extends T> data, Class<? extends T> clazz) 方法,将数据集合和数据模型类传递给 Excel 写入器,执行数据写入操作。
  5. 创建 Excel 读取器:通过 EasyExcel.read() 方法创建一个 Excel 读取器,指定要读取的 Excel 文件路径或输入流。
  6. 定义 Excel 读取监听器:创建一个 Java 类,继承 AnalysisEventListener<T>,重写 invoke() 方法,用于处理每行读取的 Excel 数据。
  7. 读取 Excel 数据:通过 read(Class<? extends T> clazz, AnalysisEventListener<T> eventListener) 方法,将数据模型类和监听器传递给 Excel 读取器,执行数据读取操作。
  8. 使用转换器:如果需要对 Excel 数据进行自定义转换,可以实现自定义的转换器,并在数据模型的注解中指定使用的转换器。
  9. 处理异常:在读取 Excel 数据和写入 Excel 数据的过程中,可能会出现异常情况,可以通过捕获和处理异常,进行相应的错误处理和日志记录。
  10. 其他功能:EasyExcel 还提供了更多功能,如数据筛选、条件格式、合并单元格、Sheet 切换等,可以根据需求灵活应用。

实现代码

**ImprotListener

核心代码

有个很重要的点 ImprotListener不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去

package com.ruoyi.project.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.project.domain.ImportDemo;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
/**
 * 导入监听器
 *
 * @author star
 * @date 2023-05-16
 */
@Slf4j
public class ImprotListener extends AnalysisEventListener<ImprotExcelVo> {

    private Logger logger = LoggerFactory.getLogger(BriefingListener.class);

    private static final int BATCH_COUNT = 100;

    // 不方便直接注入,通过构造函数引入
    private IImportService iImportService;

    // 这里方便演示理解,这里保存的日志的列表,实际中用于保存其他业务数据
    private List<Log> logList = new ArrayList<>();


    public ImprotListener(IImportService iImportService) {
        this.iImportService = iImportService;
    }

    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.info("导入错误::"+"错误行数{"+(context.getCurrentRowNum().longValue()+1)+
                "}检查内容{"+context.getCurrentRowAnalysisResult()+
                "}错误提示:"+exception.getMessage()+"}");
        throw new CustomException(
                "错误行数("+(context.getCurrentRowNum().longValue()+1)+
                        ")错误提示:"+exception.getMessage()+"}");
    }
    @Override
    public void invoke(ImprotExcelVo improtExcelVo, AnalysisContext analysisContext) {
        // 校验数据
        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        List<String> errorList = new ArrayList<>();

        Set<ConstraintViolation<ImprotExcelVo>> violations = validator.validate(improtExcelVo);
        if (!violations.isEmpty()) {
            for (ConstraintViolation<ImprotExcelVo> violation : violations) {
                errorList.add(violation.getMessage());
            }
        }

        // 如果校验不通过,则返回错误信息
        if (!errorList.isEmpty()) {
            logger.info("数据校验不通过,", errorList);
            throw new CustomException("数据校验不通过,"+ errorList);
        }

        logger.info("解析到一条数据", improtExcelVo.toString());
        logger.info("解析到的客户{}", improtExcelVo.getClient());

        if (StringUtils.isEmpty(improtExcelVo.getClient()) || "null".equals(improtExcelVo.getClient())) {
            return;
        }

        // 模拟业务操作,查找客户信息
        Client client = iImportService.findClientByName(improtExcelVo.getClient());
        logger.info("查询是否有对应的客户,{}", JSON.toJSONString(client));

        // 错误处理
        if (client == null) {
            logger.info("错误记录的案件标识{},{}", improtExcelVo.getXxxxNumber(), improtExcelVo.getExternalNumber());
            throw new CustomException("客户信息不存或错误");
        }
        // 将查询到的客户id保存到Vo中
        improtExcelVo.setClientId(client.getId());

        ImportDemo importDemo = new ImportDemo();
        importDemo.setXxxxNumber(improtExcelVo.getXxxxNumber());
        importDemo.setExternalNumber(improtExcelVo.getExternalNumber());
        List<ImportDemo> importDemoList = iImportService.queryList(importDemo);
        if (!CollectionUtils.isEmpty(importDemoList)) {
            importDemo = importDemoList.get(0);
        } else {
            importDemo = null;
        }

        // 等于空,为新增
        if (importDemo == null) {
            importDemo = new ImportDemo();
            BeanUtils.copyProperties(improtExcelVo, importDemo);

            // 业务操作,设置状态,对数据进行

            importDemo.setCreateTime(new Date());
            importDemo.setUpdateTime(new Date());
            iImportService.save(importDemo);
        } else {
            // 不为空新增
            Date createTime = importDemo.getCreateTime();
            log.info("打印execl导入的参数======:"+improtExcelVo);
            BeanUtils.copyProperties(improtExcelVo, importDemo);

            importDemo.setCreateTime(createTime);
            importDemo.setUpdateTime(new Date());
            log.info("打印更新数据的参数======:"+importDemo);
            // 根据主键id去更新数据
            iImportService.updateById(importDemo);
        }
        
        //模拟还有业务逻辑,接着操作数据,如果单一业务,可以删除下面部分-star
        Log log = new Log();
        log.setImportDemoId(importDemo.getId());
        log.setMsg("这是一个信息,用于记录导入信息");
        Log.setCreateTime(new Date());
        Log.setUpdateTime(new Date());
        logList.add(Log);
        
        if (logList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            logList.clear();
        }
        //模拟还有业务逻辑,接着操作数据,如果单一业务,可以删除下面部分-end
    }


    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        saveData();
        logger.info("所有数据解析完成!");
    }

    private void saveData() {
        logger.info("{}条数据,开始存储数据库!", logList.size());
        iLongService.saveBatch(logList);
        logger.info("存储数据库成功!");
    }
}

ImportController

package com.ruoyi.project.controller;

import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * 导入接口Controller
 *
 * @author star
 * @date 2023-05-16
 */
@Slf4j
@Api(description = "导入接口")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@RestController
@RequestMapping("/import")
public class ImportController extends BaseController {

    private final IImportService iImportService;


    @ApiOperation("导入ExeclDemo")
    @Log(title = "导入ExeclDemo", businessType = BusinessType.IMPORT)
    @PostMapping("/importExeclDemo")
    public AjaxResult importCase(@RequestParam("file") MultipartFile file) throws IOException {
        // iImportService 做为参数传递方便后续使用,传入相关 Service
        iImportService.importExecl(file.getInputStream(), ImprotExcelVo.class, new ImprotListener(iImportService));
        return toAjax(1);
    }
}

service

@Transactional 注解被应用于 importExecl 方法上,表示该方法需要在一个事务中执行。事务是数据库操作的基本单位,它要么全部成功提交,要么全部回滚,以保证数据的一致性和完整性。

通过将 @Transactional 注解应用于该方法,可以确保在执行导入操作期间,如果出现异常或错误,数据库的状态会被回滚到导入前的状态,以避免数据不一致的情况发生。

package com.ruoyi.project.service.impl;

import com.ruoyi.project.util.EasyExcelUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;

/**
 * 导入Service业务层处理
 *
 * @author star
 * @date 2023-05-16
 */
@Slf4j
@Service
public class ImportServiceImpl implements IImportService {

    @Override
    @Transactional
    public void importExecl(InputStream inputStream, Class<ImprotExcelVo> improtExcelVoClass, ImprotListener improtListener) {
        EasyExcelUtils.doRead(inputStream, improtExcelVoClass, improtListener, 1);
    }
}

ImportDemo

导入Demo对象 c_import_demo

package com.ruoyi.project.domain;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Date;

/**
 * 导入Demo对象 c_import_demo
 *
 * @author star
 * @date 2023-05-16
 */
@Data
@NoArgsConstructor
@Accessors(chain = true)
@TableName("c_import_demo")
public class ImportDemo implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * id
     */
    @TableId(value = "id")
    private Long id;

    /**
     * 时间
     * */
    private Date xxxxxTime;

    /**
     * 单号
     * */
    private String xxxxNumber;

    /**
     *外部单号
     * */
    private String externalNumber;

    /**
     *客户
     * */
    private String client;

    /**
     *标识
     * */
    private String identification;

    /**
     *姓名
     * */
    private String name;

    /**
     *证件号
     * */
    private String defendantId;

    /**
     *身份证过期情况
     * */
    private String idCardExpiration;

    /**
     *手机号码
     * */
    private String phone;

    /**
     *户籍地地址
     * */
    private String defendantAddress;

    /**
     *地址
     * */
    private String address;
}

ImprotExcelVo

导入实体类:

  • xxxxxTime: 时间字段,对应Excel中的第一列,使用自定义的日期转换器 DateConverter 进行转换。
  • xxxxNumber: 单号字段,对应Excel中的第二列。
  • externalNumber: 外部单号字段,对应Excel中的第三列。
  • client: 客户字段,对应Excel中的第四列。
  • identification: 标识字段,对应Excel中的第五列。
  • name: 姓名字段,对应Excel中的第六列,长度不能超过64。
  • defendantId: 证件号字段,对应Excel中的第七列,长度不能超过64。
  • idCardExpiration: 身份证过期情况字段,对应Excel中的第八列。
  • phone: 手机号码字段,对应Excel中的第九列,长度不能超过11。
  • defendantAddress: 户籍地地址字段,对应Excel中的第十列,长度不能超过64。
  • address: 地址字段,对应Excel中的第十一列。

@Length 是 Hibernate Validator 提供的注解,用于限制字符串字段的长度。

@Length 注解有以下参数可用:

  • min:指定字符串的最小长度,默认值为 0。
  • max:指定字符串的最大长度,默认值为 Integer.MAX_VALUE
  • message:指定验证失败时的错误消息,默认值为一个预定义的错误消息。
package com.ruoyi.project.domain.vo;

import com.alibaba.excel.annotation.ExcelProperty;
import com.ruoyi.project.converter.DateConverter;
import lombok.Data;
import lombok.ToString;
import org.hibernate.validator.constraints.Length;

import java.io.Serializable;
import java.util.Date;

@Data
@ToString
public class ImprotExcelVo implements Serializable {

    private static final long serialVersionUID = 1L;

    @ExcelProperty(value = "时间" , index = 0,converter = DateConverter.class)
    private Date xxxxxTime;

    @ExcelProperty(value = "单号", index = 1)
    private String xxxxNumber;

    @ExcelProperty(value = "外部单号", index = 2)
    private String externalNumber;

    @ExcelProperty(value = "客户", index = 3)
    private String client;

    @ExcelProperty(value = "标识", index = 4)
    private String identification;

    @ExcelProperty(value = "姓名", index = 5)
    @Length(max = 64, message = "姓名过长")
    private String name;

    @ExcelProperty(value = "证件号", index = 6)
    @Length(max = 64, message = "证件号过长")
    private String defendantId;

    @ExcelProperty(value = "身份证过期情况", index = 7)
    private String idCardExpiration;

    @ExcelProperty(value = "手机号码", index = 8)
    @Length(max = 11, message = "请输入正确的手机号码")
    private String phone;

    @ExcelProperty(value = "户籍地地址", index = 9)
    @Length(max = 64, message = "户籍地地址过长")
    private String defendantAddress;

    @ExcelProperty(value = "地址",index = 10)
    private String address;
}

DateConverter

这是一个实现了Converter接口的日期转换器类DateConverter。它用于在Excel的导入和导出操作中转换日期值。

下面是DateConverter类中方法的说明:

  1. supportJavaTypeKey()方法:返回转换器支持的Java数据类型。在这里,返回null,表示转换器支持任意Java数据类型。
  2. supportExcelTypeKey()方法:返回转换器支持的Excel数据类型。同样返回null,表示转换器支持任意Excel数据类型。
  3. convertToJavaData()方法:将Excel单元格数据转换为Java对象。通过cellData.getStringValue()获取单元格的字符串值,如果字符串值非空,则使用DateUtils.dateTime()方法将其转换为日期类型(使用指定的日期格式)。最后,返回转换后的日期对象。
  4. convertToExcelData()方法:将Java对象转换为Excel单元格数据。在此处返回null,表示不进行Excel到Java数据的转换。
package com.ruoyi.project.converter;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import java.util.Date;

public class DateConverter implements Converter {
    @Override
    public Class<?> supportJavaTypeKey() {
        return null; // 支持任意Java数据类型
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return null; // 支持任意Excel数据类型
    }

    @Override
    public Object convertToJavaData(CellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        String stringValue = cellData.getStringValue(); // 获取单元格的字符串值
        if (StringUtils.isNotEmpty(stringValue)) { // 检查字符串值是否非空
            return DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, stringValue + " 00:00:00"); // 将字符串值转换为日期类型
        }
        return null;
    }

    @Override
    public CellData<?> convertToExcelData(Object value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return null; // 不进行Excel到Java数据的转换
    }
}

EasyExcelUtils

EasyExcelUtils 是一个工具类,用于简化使用 EasyExcel 库进行 Excel 文件读写操作。通过调用 EasyExcelUtils 类的 doRead 方法实现了读取 Excel 文件的功能。

package com.ruoyi.project.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.ruoyi.common.exception.CustomException;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

/**
 * 使用EasyExcel导出Excel封装的工具类
 */
public class EasyExcelUtils {

    /**
     * 导出数据的高级封装
     * @param response
     * @param fileName
     * @param list
     */
    public static void doWriter(HttpServletResponse response, String fileName, List list){
        if(list==null||list.size()<=0){
            throw new CustomException("导出数据不能为空");
        }
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName + ".xlsx").getBytes(), "ISO8859-1"));
            EasyExcel.write(response.getOutputStream(), list.get(0).getClass()).sheet(fileName).doWrite(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出数据的高级封装
     * @param response
     * @param fileName
     * @param list
     */
    public static void doWriter(HttpServletResponse response, String fileName, String sheetName, List list){
        if(list==null||list.size()<=0){
            throw new CustomException("导出数据不能为空");
        }
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName + ".xlsx").getBytes(), "ISO8859-1"));
            EasyExcel.write(response.getOutputStream(), list.get(0).getClass()).sheet(sheetName).doWrite(list);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出Excel文件的初级版本
     * @param response
     * @param fileName
     * @param list
     */
    public static void doExportExcelV1(HttpServletResponse response, String fileName, List list){
        if(list==null||list.size()<=0){
            throw new CustomException("导出数据不能为空");
        }
        //进行导出文件操作
        OutputStream out=null;
        ExcelWriter writer=null;
        try {
            out = response.getOutputStream();
            //获取ExcelWriter对象,自定义表头信息
            writer = EasyExcel.write(out, list.get(0).getClass()).excelType(ExcelTypeEnum.XLSX).build();
            WriteSheet writeSheet = new WriteSheet();
            writeSheet.setSheetName(fileName);
            writer.write(list, writeSheet);
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + new String((fileName+ ".xlsx").getBytes(), "ISO8859-1"));
            out.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (writer != null) {
                writer.finish();
            }
            if (out != null) {
                try {
                    out.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }


    //################################################上传Excel文件相关的接口###########################################

    /**
     * 上传Excel文件功能
     * @param multipartFile
     * @param t
     * @param listener  进行自定义,主要的数据保存操作在listener当中操作,支持异步和同步操作
     * @param <T>
     */
    public static <T> void doRead(MultipartFile multipartFile, T t, AnalysisEventListener listener){
        try {
            EasyExcel.read(multipartFile.getInputStream(), t.getClass(), listener).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 读或上传Excel文件的高级封装
     * @param multipartFile
     * @param clz
     * @param listener  主要的数据保存操作在listener当中操作,支持异步和同步操作
     */
    public static void doRead(MultipartFile multipartFile, Class clz, AnalysisEventListener listener,Integer headRowNumber){
        try {
            EasyExcel.read(multipartFile.getInputStream(), clz, listener).sheet().headRowNumber(headRowNumber).doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void doRead(InputStream inputStream, Class clz, AnalysisEventListener listener,Integer headRowNumber){
       try {
            EasyExcel.read(inputStream, clz, listener).sheet()
                    .headRowNumber(headRowNumber).doRead();
        } catch (CustomException e) {
           throw new CustomException(e.getMessage());
        }
    }

}

BeanUtils

BeanUtils 是一个工具类,用于在Java对象之间进行属性拷贝。它提供了一系列静态方法,可以方便地进行对象之间的属性复制,无需手动逐个设置属性。

package com.ruoyi.common.utils.bean;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Bean 工具类
 * 
 * @author ruoyi
 */
public class BeanUtils extends org.springframework.beans.BeanUtils
{
    /** Bean方法名中属性名开始的下标 */
    private static final int BEAN_METHOD_PROP_INDEX = 3;

    /** * 匹配getter方法的正则表达式 */
    private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");

    /** * 匹配setter方法的正则表达式 */
    private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");

    /**
     * Bean属性复制工具方法。
     * 
     * @param dest 目标对象
     * @param src 源对象
     */
    public static void copyBeanProp(Object dest, Object src)
    {
        try
        {
            copyProperties(src, dest);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 获取对象的setter方法。
     * 
     * @param obj 对象
     * @return 对象的setter方法列表
     */
    public static List<Method> getSetterMethods(Object obj)
    {
        // setter方法列表
        List<Method> setterMethods = new ArrayList<Method>();

        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();

        // 查找setter方法

        for (Method method : methods)
        {
            Matcher m = SET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 1))
            {
                setterMethods.add(method);
            }
        }
        // 返回setter方法列表
        return setterMethods;
    }

    /**
     * 获取对象的getter方法。
     * 
     * @param obj 对象
     * @return 对象的getter方法列表
     */

    public static List<Method> getGetterMethods(Object obj)
    {
        // getter方法列表
        List<Method> getterMethods = new ArrayList<Method>();
        // 获取所有方法
        Method[] methods = obj.getClass().getMethods();
        // 查找getter方法
        for (Method method : methods)
        {
            Matcher m = GET_PATTERN.matcher(method.getName());
            if (m.matches() && (method.getParameterTypes().length == 0))
            {
                getterMethods.add(method);
            }
        }
        // 返回getter方法列表
        return getterMethods;
    }

    /**
     * 检查Bean方法名中的属性名是否相等。<br>
     * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
     * 
     * @param m1 方法名1
     * @param m2 方法名2
     * @return 属性名一样返回true,否则返回false
     */

    public static boolean isMethodPropEquals(String m1, String m2)
    {
        return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/531982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频格式、视频方式区别、一个视频播放的全过程

一&#xff1a;直播和点播的区别及有的格式 1&#xff1a;视频直播的播放内容是媒体直播流的形式&#xff0c;不存在物理文件&#xff0c;视频画面和声音信息都是实时采集、传输和播放的。如果没有录制模块&#xff0c;视频画面和声音播放完毕就消失了&#xff0c;不能提供二次…

并行分布式计算 并行计算性能评测

文章目录 并行分布式计算 并行计算性能评测基本性能指标参数CPU 基本性能指标存储器性能并行与存储开销 加速比性能定律Amdahl 定律Gustafson 定律Sun 和 Ni 定律加速比讨论 可括放性评测标准等效率度量标准等速度度量标准平均延迟度量标准 基准评测程序&#xff08;Benchmark&…

甘特图控件DHTMLX Gantt入门使用教程【引入】:用Node.js实现Gantt(下)

DHTMLX Gantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的大部分开发需求&#xff0c;具备完善的甘特图图表库&#xff0c;功能强大&#xff0c;价格便宜&#xff0c;提供丰富而灵活的JavaScript API接口&#xff0c;与各种服务器端技术&am…

Linux多路转接之select

文章目录 一、IO的理解二、五种IO模型1.阻塞式IO2.非阻塞式IO3.信号驱动式IO4.IO多路转接5.异步IO6.五种IO模型的总结 三、非阻塞式IO1.fcntl函数 四、IO多路转接之select的介绍五、编写select服务器1.将获取连接时设置为select多路转接2.获取连接成功后的读取数据 六、select多…

商家订单之Java版SpringCloud+SpringBoot+Mybatis+Vue+Uniapp 分布式、微服务、多商家入驻b2b2c电子商务云平台

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案&#xff1f;以下是我结合公司的产品做的总结&#xff0c;希望可以帮助到大家&#xff01; 搜索体验小程序&#xff1a;海哇 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买…

linu0.12-9-blk_drv

[394页] 第9章 块设备驱动程序 395–9-1-总体功能 395–9-1-1-块设备请求项和请求队列 397–9-1-2-块设备访问调度处理 397–9-1-3-块设备操作方式 398–9-2-blk.h文件 398–9-2-1-功能描述 399–9-2-2-代码注释 403–9-3-hd.c程序 403–9-3-1-功能描述 405–9-3-2-代码注释…

离散数学_九章:关系(6)

&#x1fa90;9.6 偏序 1、⛺偏序关系和偏序集⛲偏序关系⛲偏序&#xff08;关系&#xff09;的例子 a. “大于或等于” 关系b. “整除” 关系c. “包含” 关系 &#x1f3ac;偏序集&#x1f3ac;可比性&#xff08;comparability&#xff09; " ≼ " 符号a. 可比 &a…

【工具】如何判断两个二进制文件是否相同

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…

wx自定义组件

自定义组件的意义&#xff1a; 提供一系列的样式&#xff0c;&#xff0c;通用的样式提供一系列的骨架&#xff0c;&#xff0c;通用标签避免重复写一些业务逻辑 小程序自定义组件中 &#xff1a; slot &#xff1a; 修改组件内容 外部样式类&#xff1a; 修改组件样式 slot…

案例7:Java茶叶销售网站设计与实现开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

手术麻醉信息管理系统源码(简称手麻系统源码)php + mysql + vue2 B/S网页版

手术麻醉信息管理系统源码&#xff08;简称手麻系统源码&#xff09; 手术麻醉信息管理系统&#xff08;简称手麻系统&#xff09;是指专用于住院患者手术与麻醉的申请、审批、安排&#xff0c;术前、术中和术后有关信息的记录和跟踪以及手术麻醉室内部管理等功能的计算机应用…

Lucene中的Field域、索引维护、搜索、相关度排序和中文分词器讲解

Field域 Field属性 Field是文档中的域&#xff0c;包括Field名和Field值两部分&#xff0c;一个文档可以包括多个Field&#xff0c;Document只是Field的一个承载体&#xff0c;Field值即为要索引的内容&#xff0c;也是要搜索的内容。 是否分词(tokenized) 是&#xff1a;作…

事实证明,国产BI软件的财务数据分析性价比极高!

国产BI软件做财务数据分析的性价比极高&#xff0c;主要得益于两个因素&#xff0c;一个是国产BI软件按功能模块购买&#xff0c;大幅度降低BI大数据分析平台的使用成本&#xff1b;另一个则是国产BI软件已打磨出标准化、系统化的财务数据分析方案&#xff0c;低成本、低风险、…

Blender基础技巧小结

官网下载 https://www.blender.org/download/lts/2-83/ 我下载的版本&#xff1a;LTS Release 2.83.20 Windows – Portable Ogre导出插件 https://github.com/OGRECave/blender2ogre 安装插件 将blender2ogre\io_ogre复制到&#xff1a;blender-2.83.20-windows-x64\2.8…

论文阅读-17-Deep Long-Tailed Learning: A Survey---3.1Class Re-balancing

文章目录 1. Re-sampling1.1 Class-balanced re-sampling(1) Decoupling① 网络架构② Sampling策略③ Classifier的学习策略 (2) SimCal① 比较 (3) DCL(4) Balanced meta-softmax(5) FASA(6) LOCE(7) VideoLT 1.2 Scheme-oriented sampling(1) LMLE(2) PRS(3) BBN(4) LTML(5)…

技术赋能光伏组件检测“大尺寸” “高精度”,维视智造SNEC亮点抢先看!

2023.5.24-5.26 全球最具影响力的 国际化、专业化、规模化光伏盛会 第十六届&#xff08;2023&#xff09; SNEC光伏大会暨(上海)展览会 即将开展 维视智造深耕机器视觉行业20年 解决方案落地众多光伏头部企业 如今作为光伏组件视觉检测系统行业领先者 此次展会维视将…

(文章复现)《高比例清洁能源接入下计及需求响应的配电网重构》(含matlab代码)

1.引言 配电网重构作为配电网优化运行的手段之一&#xff0c;通过改变配电网的拓扑结构&#xff0c;以达到降低网损、改善电压分布、提升系统的可靠性与经济性等目的。近年来&#xff0c;随着全球能源消耗快速增长以及环境的日趋恶化&#xff0c;清洁能源飞速发展&#xff0c;分…

从【创作者】转变为【博客专家】-- 内含详细申请过程

从【创作者】转变为【博客专家】 0、引言1、创作身份认证1.1 起因1.2 违背祖宗的决定1.3 认证创作身份1.3.0 好处1.3.1 条件1.3.2 认证信息1.3.3 后台审核 2、博客专家认证2.1 好处2.2 条件2.3 认证信息2.4 后台审核2.5 实体证书 3、 反思与总结 ⚠申明&#xff1a; 未经许可&a…

哪个年龄段人群喜欢养宠物?18-25岁占比最高,达31%

上一期&#xff0c;我们通过可视化互动平台分析了萌宠经济下宠物食品的发展现状&#xff0c;这一期我们接着来分析一下&#xff0c;在萌宠经济下&#xff0c;我国宠物医疗产业的市场情况。 由于现在很多家庭都喜欢饲养宠物&#xff0c;宠物数量的快速增长从而拉动了宠物经济的…

晶飞FLA5000光谱仪.FlaSpec文件数据解析

引言 首先说明下晶飞上位机软件存在的问题&#xff0c;实验所采用的FLA5000型号光谱仪&#xff0c;光谱波段从280-970nm&#xff0c;FWHM值为2.4nm。 1、上位机软件中的光谱数据复制功能基本是废的&#xff0c;最多只能到599.9nm&#xff0c;后面的数据全部消失。 2、上位机软…