POI报表的高级应用

 POI报表的高级应用

掌握基于模板打印的POI报表导出理解自定义工具类的执行流程

熟练使用SXSSFWorkbook完成百万数据报表打印理解基于事件驱动的POI报表导入

模板打印

概述

自定义生成Excel报表文件还是有很多不尽如意的地方,特别是针对复杂报表头,单元格样式,字体等操作。手写这些代码不仅费时费力,有时候效果还不太理想。那怎么样才能更方便的对报表样式,报表头进行处理呢?答案是 使用已经准备好的Excel模板,只需要关注模板中的数据即可。

模板打印的操作步骤

  • 制作模版文件(模版文件的路径)
  • 导入(加载)模版文件,从而得到一个工作簿
  • 读取工作表
  • 读取行
  • 读取单元格
  • 读取单元格样式
  • 设置单元格内容
  • 其他单元格就可以使用读到的样式了

代码实现

   /*** 采用模板打印的形式完成报表生成*      模板*  参数:*      年月-月(2018-02%)**      sxssf对象不支持模板打印*/@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable String month) throws Exception {//1.获取报表数据List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month);//2.加载模板Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");FileInputStream fis = new FileInputStream(resource.getFile());//3.通过工具类完成下载
//        new ExcelExportUtil(EmployeeReportResult.class,2,2).
//                export(response,fis,list,month+"人事报表.xlsx");//3.根据模板创建工作簿Workbook wb = new XSSFWorkbook(fis);//4.读取工作表Sheet sheet = wb.getSheetAt(0);//5.抽取公共样式Row row = sheet.getRow(2);CellStyle styles [] = new CellStyle[row.getLastCellNum()];for(int i=0;i<row.getLastCellNum();i++) {Cell cell = row.getCell(i);styles[i] = cell.getCellStyle();}//6.构造单元格int rowIndex = 2;Cell cell=null;for(int i=0;i<10000;i++) {for (EmployeeReportResult employeeReportResult : list) {row = sheet.createRow(rowIndex++);// 编号,cell = row.createCell(0);cell.setCellValue(employeeReportResult.getUserId());cell.setCellStyle(styles[0]);// 姓名,cell = row.createCell(1);cell.setCellValue(employeeReportResult.getUsername());cell.setCellStyle(styles[1]);// 手机,cell = row.createCell(2);cell.setCellValue(employeeReportResult.getMobile());cell.setCellStyle(styles[2]);// 最高学历,cell = row.createCell(3);cell.setCellValue(employeeReportResult.getTheHighestDegreeOfEducation());cell.setCellStyle(styles[3]);// 国家地区,cell = row.createCell(4);cell.setCellValue(employeeReportResult.getNationalArea());cell.setCellStyle(styles[4]);// 护照号,cell = row.createCell(5);cell.setCellValue(employeeReportResult.getPassportNo());cell.setCellStyle(styles[5]);// 籍贯,cell = row.createCell(6);cell.setCellValue(employeeReportResult.getNativePlace());cell.setCellStyle(styles[6]);// 生日,cell = row.createCell(7);cell.setCellValue(employeeReportResult.getBirthday());cell.setCellStyle(styles[7]);// 属相,cell = row.createCell(8);cell.setCellValue(employeeReportResult.getZodiac());cell.setCellStyle(styles[8]);// 入职时间,cell = row.createCell(9);cell.setCellValue(employeeReportResult.getTimeOfEntry());cell.setCellStyle(styles[9]);// 离职类型,cell = row.createCell(10);cell.setCellValue(employeeReportResult.getTypeOfTurnover());cell.setCellStyle(styles[10]);// 离职原因,cell = row.createCell(11);cell.setCellValue(employeeReportResult.getReasonsForLeaving());cell.setCellStyle(styles[11]);// 离职时间cell = row.createCell(12);cell.setCellValue(employeeReportResult.getResignationTime());cell.setCellStyle(styles[12]);}}//7.下载//3.完成下载ByteArrayOutputStream os = new ByteArrayOutputStream();wb.write(os);new DownloadUtils().download(os,response,month+"人事报表.xlsx");}

骚戴理解:学会了一个小技巧,怎么获取模板数据?

Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");
FileInputStream fis = new FileInputStream(resource.getFile());
Workbook wb = new XSSFWorkbook(fis);

同时这里的 for(int i=0;i<10000;i++) 其实是为了造数据,数据库本来查询到的数据有90多条,然后循环10000次自然就是90多万条,接近百万数据量

自定义工具类

自定义注解ExcelAttribute

package com.ihrm.domain.poi;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {/** 对应的列名称 */String name() default "";/** 列序号 */int sort();/** 字段类型对应的格式 */String format() default "";}

导出工具类

package com.ihrm.common.poi.utils;import com.ihrm.domain.poi.ExcelAttribute;
import lombok.Getter;
import lombok.Setter;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;@Getter
@Setter
public class ExcelExportUtil<T> {private int rowIndex;private int styleIndex;private String templatePath;private Class clazz;private  Field fields[];public ExcelExportUtil(Class clazz,int rowIndex,int styleIndex) {this.clazz = clazz;this.rowIndex = rowIndex;this.styleIndex = styleIndex;fields = clazz.getDeclaredFields();}/*** 基于注解导出*/public void export(HttpServletResponse response,InputStream is, List<T> objs,String fileName) throws Exception {XSSFWorkbook workbook = new XSSFWorkbook(is);Sheet sheet = workbook.getSheetAt(0);CellStyle[] styles = getTemplateStyles(sheet.getRow(styleIndex));AtomicInteger datasAi = new AtomicInteger(rowIndex);for (T t : objs) {Row row = sheet.createRow(datasAi.getAndIncrement());for(int i=0;i<styles.length;i++) {Cell cell = row.createCell(i);cell.setCellStyle(styles[i]);for (Field field : fields) {if(field.isAnnotationPresent(ExcelAttribute.class)){field.setAccessible(true);ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);if(i == ea.sort()) {cell.setCellValue(field.get(t).toString());}}}}}fileName = URLEncoder.encode(fileName, "UTF-8");response.setContentType("application/octet-stream");response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));response.setHeader("filename", fileName);workbook.write(response.getOutputStream());}public CellStyle[] getTemplateStyles(Row row) {CellStyle [] styles = new CellStyle[row.getLastCellNum()];for(int i=0;i<row.getLastCellNum();i++) {styles[i] = row.getCell(i).getCellStyle();}return styles;}
}

导入工具类

package com.ihrm.common.poi.utils;import com.ihrm.domain.poi.ExcelAttribute;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.format.CellFormat;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class ExcelImportUtil<T> {private Class clazz;private  Field fields[];public ExcelImportUtil(Class clazz) {this.clazz = clazz;fields = clazz.getDeclaredFields();}/*** 基于注解读取excel*/public List<T> readExcel(InputStream is, int rowIndex,int cellIndex) {List<T> list = new ArrayList<T>();T entity = null;try {XSSFWorkbook workbook = new XSSFWorkbook(is);Sheet sheet = workbook.getSheetAt(0);// 不准确int rowLength = sheet.getLastRowNum();System.out.println(sheet.getLastRowNum());for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {Row row = sheet.getRow(rowNum);entity = (T) clazz.newInstance();System.out.println(row.getLastCellNum());for (int j = cellIndex; j < row.getLastCellNum(); j++) {Cell cell = row.getCell(j);for (Field field : fields) {if(field.isAnnotationPresent(ExcelAttribute.class)){field.setAccessible(true);ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);if(j == ea.sort()) {field.set(entity, covertAttrType(field, cell));}}}}list.add(entity);}} catch (Exception e) {e.printStackTrace();}return list;}/*** 类型转换 将cell 单元格格式转为 字段类型*/private Object covertAttrType(Field field, Cell cell) throws Exception {String fieldType = field.getType().getSimpleName();if ("String".equals(fieldType)) {return getValue(cell);}else if ("Date".equals(fieldType)) {return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getValue(cell)) ;}else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {return Integer.parseInt(getValue(cell));}else if ("double".equals(fieldType) || "Double".equals(fieldType)) {return Double.parseDouble(getValue(cell));}else {return null;}}/*** 格式转为String* @param cell* @return*/public String getValue(Cell cell) {if (cell == null) {return "";}switch (cell.getCellType()) {case STRING:return cell.getRichStringCellValue().getString().trim();case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {Date dt = DateUtil.getJavaDate(cell.getNumericCellValue());return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(dt);} else {// 防止数值变成科学计数法String strCell = "";Double num = cell.getNumericCellValue();BigDecimal bd = new BigDecimal(num.toString());if (bd != null) {strCell = bd.toPlainString();}// 去除 浮点型 自动加的 .0if (strCell.endsWith(".0")) {strCell = strCell.substring(0, strCell.indexOf("."));}return strCell;}case BOOLEAN:return String.valueOf(cell.getBooleanCellValue());default:return "";}}
}

工具类完成导入导出

导入数据

给导入的User实体类上面加上自定义注解 @ExcelAttribute

package com.ihrm.domain.system;import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ihrm.domain.poi.ExcelAttribute;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;import javax.persistence.*;
import java.io.Serializable;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;/*** 用户实体类*/
@Entity
@Table(name = "bs_user")
@Getter
@Setter
@NoArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = 4297464181093070302L;/*** ID*/@Idprivate String id;/*** 手机号码*/@ExcelAttribute(sort = 2)private String mobile;/*** 用户名称*/@ExcelAttribute(sort = 1)private String username;/*** 密码*/private String password;/*** 启用状态 0为禁用 1为启用*/private Integer enableState;/*** 创建时间*/private Date createTime;private String companyId;private String companyName;/*** 部门ID*/@ExcelAttribute(sort = 6)private String departmentId;/*** 入职时间*/@ExcelAttribute(sort = 5)private Date timeOfEntry;/*** 聘用形式*/@ExcelAttribute(sort = 4)private Integer formOfEmployment;/*** 工号*/@ExcelAttribute(sort = 3)private String workNumber;/*** 管理形式*/private String formOfManagement;/*** 工作城市*/private String workingCity;/*** 转正时间*/private Date correctionTime;/*** 在职状态 1.在职  2.离职*/private Integer inServiceStatus;private String departmentName;/*** level*     String*          saasAdmin:saas管理员具备所有权限*          coAdmin:企业管理(创建租户企业的时候添加)*          user:普通用户(需要分配角色)*/private String level;private String staffPhoto;  //用户头像public User(Object [] values) {//用户名    手机号    工号    聘用 形式    入职 时间    部门编码this.username = values[1].toString();this.mobile = values[2].toString();this.workNumber = new DecimalFormat("#").format(values[3]);this.formOfEmployment =((Double) values[4]).intValue();this.timeOfEntry = (Date) values[5];this.departmentId = values[6].toString(); //部门编码 != 部门id}/***  JsonIgnore*     : 忽略json转化*/@JsonIgnore@ManyToMany@JoinTable(name="pe_user_role",joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")},inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")})private Set<Role> roles = new HashSet<Role>();//用户与角色   多对多
}
  • 在UserController中importUser方法用工具类来导入数据
    /*** 导入Excel,添加用户*/@RequestMapping(value = "/user/import" , method = RequestMethod.POST)public Result importUser(@RequestParam(name = "file") MultipartFile file) throws Exception {List<User> list = new ExcelImportUtil(User.class).readExcel(file.getInputStream(), 1, 1);//3.批量保存用户userService.saveAll(list , companyId , companyName);return new Result(ResultCode.SUCCESS);}

骚戴理解:这里也只要知道怎么用这个工具类即可

  • User.class是指的导出实体类的字节码
  • file.getInputStream()是指的传入文件的IO输入流
  • 第一个1是指从模板的第二行开始读数据
  • 第二个1是指从模板的第二列开始读数据

导出数据

给导出的实体类上面加上自定义注解 @ExcelAttribute

package com.ihrm.domain.employee.response;import com.ihrm.domain.employee.EmployeeResignation;
import com.ihrm.domain.employee.UserCompanyPersonal;
import com.ihrm.domain.poi.ExcelAttribute;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.BeanUtils;@Getter
@Setter
@NoArgsConstructor
@ToString
public class EmployeeReportResult {@ExcelAttribute(sort = 0)private String userId;@ExcelAttribute(sort = 1)private String username;private String departmentName;@ExcelAttribute(sort = 2)private String mobile;@ExcelAttribute(sort = 9)private String timeOfEntry;private String companyId;private String sex;/*** 出生日期*/private String dateOfBirth;/*** 最高学历*/@ExcelAttribute(sort = 3)private String theHighestDegreeOfEducation;/*** 国家地区*/@ExcelAttribute(sort = 4)private String nationalArea;/*** 护照号*/@ExcelAttribute(sort = 5)private String passportNo;/*** 身份证号*/private String idNumber;/*** 身份证照片-正面*/private String idCardPhotoPositive;/*** 身份证照片-背面*/private String idCardPhotoBack;/*** 籍贯*/@ExcelAttribute(sort = 6)private String nativePlace;/*** 民族*/private String nation;/*** 英文名*/private String englishName;/*** 婚姻状况*/private String maritalStatus;/*** 员工照片*/private String staffPhoto;/*** 生日*/@ExcelAttribute(sort = 7)private String birthday;/*** 属相*/@ExcelAttribute(sort = 8)private String zodiac;/*** 年龄*/private String age;/*** 星座*/private String constellation;/*** 血型*/private String bloodType;/*** 户籍所在地*/private String domicile;/*** 政治面貌*/private String politicalOutlook;/*** 入党时间*/private String timeToJoinTheParty;/*** 存档机构*/private String archivingOrganization;/*** 子女状态*/private String stateOfChildren;/*** 子女有无商业保险*/private String doChildrenHaveCommercialInsurance;/*** 有无违法违纪行为*/private String isThereAnyViolationOfLawOrDiscipline;/*** 有无重大病史*/private String areThereAnyMajorMedicalHistories;/*** QQ*/private String qq;/*** 微信*/private String wechat;/*** 居住证城市*/private String residenceCardCity;/*** 居住证办理日期*/private String dateOfResidencePermit;/*** 居住证截止日期*/private String residencePermitDeadline;/*** 现居住地*/private String placeOfResidence;/*** 通讯地址*/private String postalAddress;/*** 联系手机*/private String contactTheMobilePhone;/*** 个人邮箱*/private String personalMailbox;/*** 紧急联系人*/private String emergencyContact;/*** 紧急联系电话*/private String emergencyContactNumber;/*** 社保电脑号*/private String socialSecurityComputerNumber;/*** 公积金账号*/private String providentFundAccount;/*** 银行卡号*/private String bankCardNumber;/*** 开户行*/private String openingBank;/*** 学历类型*/private String educationalType;/*** 毕业学校*/private String graduateSchool;/*** 入学时间*/private String enrolmentTime;/*** 毕业时间*/private String graduationTime;/*** 专业*/private String major;/*** 毕业证书*/private String graduationCertificate;/*** 学位证书*/private String certificateOfAcademicDegree;/*** 上家公司*/private String homeCompany;/*** 职称*/private String title;/*** 简历*/private String resume;/*** 有无竞业限制*/private String isThereAnyCompetitionRestriction;/*** 前公司离职证明*/private String proofOfDepartureOfFormerCompany;/*** 备注*/private String remarks;/*** 离职时间*/@ExcelAttribute(sort = 12)private String resignationTime;/*** 离职类型*/@ExcelAttribute(sort = 10)private String typeOfTurnover;/*** 申请离职原因*/@ExcelAttribute(sort = 11)private String reasonsForLeaving;public EmployeeReportResult(UserCompanyPersonal personal, EmployeeResignation resignation) {BeanUtils.copyProperties(personal,this);if(resignation != null) {BeanUtils.copyProperties(resignation,this);}}
}

骚戴理解: @ExcelAttribute注解就是用来定义实体类属性在模板中列的位置,例如模板中姓名对应的列号是1,所以 @ExcelAttribute(sort = 1) private String username;里的sort = 1,注意列号是从0开始的

在export方法里使用导入工具类

    @RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable String month) throws Exception {//1.获取报表数据List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month+"%");//2.加载模板Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");FileInputStream fis = new FileInputStream(resource.getFile());//3.通过工具类完成下载new ExcelExportUtil(EmployeeReportResult.class,2,2).export(response,fis,list,month+"人事报表.xlsx");}

骚戴理解:下面的工具类会用就行,知道传入的参数是什么就可以了

new ExcelExportUtil(EmployeeReportResult.class,2,2).export(response,fis,list,month+"人事报表.xlsx");
  • EmployeeReportResult.class是导出的实体类的字节码,也就是从数据库中查询到的数据集合的实体类的字节码
  • 第一个2是指的行号,也就是把数据集合中的数据从第几行开始写到Excel表格中,注意这个2是指的把数据从第3行开始写入,因为下标是从0开始的
  • 第二个2是指的样式的行号,可以看到我希望所有的数据样式都是如下面的第三行的样式一样,所以这个2是指的第三行的样式

  • response其实就是HttpServletResponse
  • fis是模板样式的文件输入IO流
 FileInputStream fis = new FileInputStream(resource.getFile());
  • list是需要导出数据库中查询到的所有集合数据
  List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month+"%");
  • month+"人事报表.xlsx"是导出文件的名称

百万数据报表概述

概述

我们都知道Excel可以分为早期的Excel2003版本(使用POI的HSSF对象操作)和Excel2007版本(使用POI的XSSF 操作),两者对百万数据的支持如下:

  • Excel 2003:在POI中使用HSSF对象时,excel 2003最多只允许存储65536条数据,一般用来处理较少的数据量。这时对于百万级别数据,Excel肯定容纳不了。
  • Excel 2007:当POI升级到XSSF对象时,它可以直接支持excel2007以上版本,因为它采用ooxml格式。这时excel可以支持1048576条数据,单个sheet表就支持近百万条数据。但实际运行时还可能存在问题,原因是执 行POI报表所产生的行对象,单元格对象,字体对象,他们都不会销毁,这就导致OOM的风险。

JDK性能监控工具介绍

没有性能监控工具一切推论都只能停留在理论阶段,我们可以使用Java的性能监控工具来监视程序的运行情况,包 括CUP,垃圾回收,内存的分配和使用情况,这让程序的运行阶段变得更加可控,也可以用来证明我们的推测。这里我们使用JDK提供的性能工具Jvisualvm来监控程序运行。

Jvisualvm概述

VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈

Jvisualvm的位置

Jvisualvm位于JAVA_HOME/bin目录下,直接双击就可以打开该程序。如果只是监控本地的java进程,是不需要配 置参数的,直接打开就能够进行监控。首先我们需要在本地打开一个Java程序,例如我打开员工微服务进程,这时 在jvisualvm界面就可以看到与IDEA相关的Java进程了:

骚戴理解:我的Jvisualvm默认路径在C:\Program Files\Java\jdk1.8.0_351\bin\jvisualvm.exe

Jvisualvm的使用

Jvisualvm使用起来比较简单,双击点击当前运行的进程即可进入到程序的监控界面

概述:可以看到进程的启动参数。

监视:

  • 左上:cpu利用率,gc状态的监控
  • 右上:堆利用率,永久内存区的利用率
  • 左下:类的监控
  • 右下: 线程的监控

线程:能够显示线程的名称和运行的状态,在调试多线程时必不可少,而且可以点进一个线程查看这个线程 的详细运行情况

解决方案分析

对于百万数据量的Excel导入导出,只讨论基于Excel2007的解决方法。在ApachePoi 官方提供了对操作大数据量的导入导出的工具和解决办法,操作Excel2007使用XSSF对象,可以分为三种模式:

  • 用户模式:用户模式有许多封装好的方法操作简单,但创建太多的对象,非常耗内存(之前使用的方法)
  • 事件模式:基于SAX方式解析XML,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文 档,一边扫描,一边解析。
  • SXSSF对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel

这是一张Apache POI官方提供的图片,描述了基于用户模式,事件模式,以及使用SXSSF三种方式操作Excel的特性以及CUP和内存占用情况。

骚戴理解:用户模式就是之前写的那种代码就是用户模式,如下所示就是用户模式

   /*** 采用模板打印的形式完成报表生成*      模板*  参数:*      年月-月(2018-02%)**      sxssf对象不支持模板打印*/@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable String month) throws Exception {//1.获取报表数据List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month);//2.加载模板Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");FileInputStream fis = new FileInputStream(resource.getFile());//3.通过工具类完成下载
//        new ExcelExportUtil(EmployeeReportResult.class,2,2).
//                export(response,fis,list,month+"人事报表.xlsx");//3.根据模板创建工作簿Workbook wb = new XSSFWorkbook(fis);//4.读取工作表Sheet sheet = wb.getSheetAt(0);//5.抽取公共样式Row row = sheet.getRow(2);CellStyle styles [] = new CellStyle[row.getLastCellNum()];for(int i=0;i<row.getLastCellNum();i++) {Cell cell = row.getCell(i);styles[i] = cell.getCellStyle();}//6.构造单元格int rowIndex = 2;Cell cell=null;for(int i=0;i<10000;i++) {for (EmployeeReportResult employeeReportResult : list) {row = sheet.createRow(rowIndex++);// 编号,cell = row.createCell(0);cell.setCellValue(employeeReportResult.getUserId());cell.setCellStyle(styles[0]);// 姓名,cell = row.createCell(1);cell.setCellValue(employeeReportResult.getUsername());cell.setCellStyle(styles[1]);// 手机,cell = row.createCell(2);cell.setCellValue(employeeReportResult.getMobile());cell.setCellStyle(styles[2]);// 最高学历,cell = row.createCell(3);cell.setCellValue(employeeReportResult.getTheHighestDegreeOfEducation());cell.setCellStyle(styles[3]);// 国家地区,cell = row.createCell(4);cell.setCellValue(employeeReportResult.getNationalArea());cell.setCellStyle(styles[4]);// 护照号,cell = row.createCell(5);cell.setCellValue(employeeReportResult.getPassportNo());cell.setCellStyle(styles[5]);// 籍贯,cell = row.createCell(6);cell.setCellValue(employeeReportResult.getNativePlace());cell.setCellStyle(styles[6]);// 生日,cell = row.createCell(7);cell.setCellValue(employeeReportResult.getBirthday());cell.setCellStyle(styles[7]);// 属相,cell = row.createCell(8);cell.setCellValue(employeeReportResult.getZodiac());cell.setCellStyle(styles[8]);// 入职时间,cell = row.createCell(9);cell.setCellValue(employeeReportResult.getTimeOfEntry());cell.setCellStyle(styles[9]);// 离职类型,cell = row.createCell(10);cell.setCellValue(employeeReportResult.getTypeOfTurnover());cell.setCellStyle(styles[10]);// 离职原因,cell = row.createCell(11);cell.setCellValue(employeeReportResult.getReasonsForLeaving());cell.setCellStyle(styles[11]);// 离职时间cell = row.createCell(12);cell.setCellValue(employeeReportResult.getResignationTime());cell.setCellStyle(styles[12]);}}//7.下载//3.完成下载ByteArrayOutputStream os = new ByteArrayOutputStream();wb.write(os);new DownloadUtils().download(os,response,month+"人事报表.xlsx");}

事件模式适用于大数据量的导入操作,SXSSF对象适用于大数据量的导出操作

百万数据报表导出

需求分析

使用Apache POI完成百万数据量的Excel报表导出

解决方案

思路分析

基于XSSFWork导出Excel报表,是通过将所有单元格对象保存到内存中,当所有的Excel单元格全部创建完成之后 一次性写入到Excel并导出。当百万数据级别的Excel导出时,随着表格的不断创建,内存中对象越来越多,直至内 存溢出。Apache Poi提供了SXSSFWork对象,专门用于处理大数据量Excel报表导出。

原理分析

在实例化SXSSFWork这个对象时,可以指定在内存中所产生的POI导出相关对象的数量(默认100),一旦内存中的对象的个数达到这个指定值时,就将内存中的这些对象的内容写入到磁盘中(XML的临时文件格式),就可以将这些对象从内存中销毁,以后只要达到这个值,就会以类似的处理方式处理,直至Excel导出完成。

代码实现

在原有代码的基础上替换之前的XSSFWorkbook,使用SXSSFWorkbook完成创建过程即可

   /*** 采用模板打印的形式完成报表生成*      模板*  参数:*      年月-月(2018-02%)**      sxssf对象不支持模板打印*/@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable String month) throws Exception {//1.获取报表数据List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month);//2.加载模板Resource resource = new ClassPathResource("excel-template/hr-demo.xlsx");FileInputStream fis = new FileInputStream(resource.getFile());//3.通过工具类完成下载
//        new ExcelExportUtil(EmployeeReportResult.class,2,2).
//                export(response,fis,list,month+"人事报表.xlsx");//3.根据模板创建工作簿SXSSFWorkbook wb = new SXSSFWorkbook(fis);//4.读取工作表Sheet sheet = wb.getSheetAt(0);//5.抽取公共样式Row row = sheet.getRow(2);CellStyle styles [] = new CellStyle[row.getLastCellNum()];for(int i=0;i<row.getLastCellNum();i++) {Cell cell = row.getCell(i);styles[i] = cell.getCellStyle();}//6.构造单元格int rowIndex = 2;Cell cell=null;for(int i=0;i<10000;i++) {for (EmployeeReportResult employeeReportResult : list) {row = sheet.createRow(rowIndex++);// 编号,cell = row.createCell(0);cell.setCellValue(employeeReportResult.getUserId());cell.setCellStyle(styles[0]);// 姓名,cell = row.createCell(1);cell.setCellValue(employeeReportResult.getUsername());cell.setCellStyle(styles[1]);// 手机,cell = row.createCell(2);cell.setCellValue(employeeReportResult.getMobile());cell.setCellStyle(styles[2]);// 最高学历,cell = row.createCell(3);cell.setCellValue(employeeReportResult.getTheHighestDegreeOfEducation());cell.setCellStyle(styles[3]);// 国家地区,cell = row.createCell(4);cell.setCellValue(employeeReportResult.getNationalArea());cell.setCellStyle(styles[4]);// 护照号,cell = row.createCell(5);cell.setCellValue(employeeReportResult.getPassportNo());cell.setCellStyle(styles[5]);// 籍贯,cell = row.createCell(6);cell.setCellValue(employeeReportResult.getNativePlace());cell.setCellStyle(styles[6]);// 生日,cell = row.createCell(7);cell.setCellValue(employeeReportResult.getBirthday());cell.setCellStyle(styles[7]);// 属相,cell = row.createCell(8);cell.setCellValue(employeeReportResult.getZodiac());cell.setCellStyle(styles[8]);// 入职时间,cell = row.createCell(9);cell.setCellValue(employeeReportResult.getTimeOfEntry());cell.setCellStyle(styles[9]);// 离职类型,cell = row.createCell(10);cell.setCellValue(employeeReportResult.getTypeOfTurnover());cell.setCellStyle(styles[10]);// 离职原因,cell = row.createCell(11);cell.setCellValue(employeeReportResult.getReasonsForLeaving());cell.setCellStyle(styles[11]);// 离职时间cell = row.createCell(12);cell.setCellValue(employeeReportResult.getResignationTime());cell.setCellStyle(styles[12]);}}//7.下载//3.完成下载ByteArrayOutputStream os = new ByteArrayOutputStream();wb.write(os);new DownloadUtils().download(os,response,month+"人事报表.xlsx");}

对比测试

  • XSSFWorkbook生成百万数据报表

使用XSSFWorkbook生成Excel报表,时间较长,随着时间推移,内存占用原来越多,直至内存溢出

  • SXSSFWorkbook生成百万数据报表

使用SXSSFWorkbook生成Excel报表,内存占用比较平缓

骚戴理解:可以看到SXSSFWorkbook的堆内存变化,即蓝色的曲线图,可以看出它是一个个的山峰一样的图形,上升后会下降,下降的原因是因为对象达到了SXSSFWorkbook设置的阈值,默认是100,也就是达到了100个对象后就会把对象写到临时的xml文件里面,然后销毁掉内存中的这部分对象,所以才会下降,这样的确可以实现百万数据量的导出,但是也是有一些问题的,由于内存速度速度和磁盘速度是不一样的,首先主线程会把所有的对象创建好放到内存里,然后达到阈值才会写入xml临时文件,这是两个过程,然后通常内存写入的速度远远快于磁盘写入xml临时文件的速度,所以如果数据量是很多,例如千万级别的数据量的时候还是会把内存给挤爆,然后报错OOM内存溢出!

百万数据报表读取

需求分析

使用POI基于事件模式解析案例提供的Excel文件

解决方案

思路分析

用户模式:加载并读取Excel时,是通过一次性的将所有数据加载到内存中再去解析每个单元格内容。当Excel 数据量较大时,由于不同的运行环境可能会造成内存不足甚至OOM异常。

事件模式:它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据完全存储在内存中,这对于大型文档的解析是个巨大优势。

步骤分析

  • 设置POI的事件模式
    • 根据Excel获取文件流
    • 根据文件流创建OPCPackage
    • 创建XSSFReader对象
  • Sax解析
    • 自定义Sheet处理器
    • 创建Sax的XmlReader对象
    • 设置Sheet的事件处理器
    • 逐行读取

原理分析

我们都知道对于Excel2007的实质是一种特殊的XML存储数据,那就可以使用基于SAX的方式解析XML完成Excel的读取。SAX提供了一种从XML文档中读取数据的机制。它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据完全存储在内存中,这对于大型文档的解析是个巨大优势

骚戴理解:事件驱动就是一边解析一边用,所以数据不完全放内存里,即拿即用,用完就删,所以对内存的占用很少,但是只能解析一次,因为数据会被删掉,不可逆!

代码实现

自定义处理器

package cn.itcast.poi.entity.cn.itcast.poi.handler;import cn.itcast.poi.entity.PoiEntity;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;/*** 自定义的事件处理器*  处理每一行数据读取*      实现接口*/
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {private PoiEntity entity;/*** 当开始解析某一行的时候触发*      i:行索引*/@Overridepublic void startRow(int i) {//实例化对象if(i>0) {entity = new PoiEntity();}}/*** 当结束解析某一行的时候触发*      i:行索引*/@Overridepublic void endRow(int i) {//使用对象进行业务操作System.out.println(entity);}/*** 对行中的每一个表格进行处理*      cellReference: 单元格名称*      value:数据*      xssfComment:批注*/@Overridepublic void cell(String cellReference, String value, XSSFComment xssfComment) {//对对象属性赋值if(entity != null) {String pix = cellReference.substring(0,1);switch (pix) {case "A":entity.setId(value);break;case "B":entity.setBreast(value);break;case "C":entity.setAdipocytes(value);break;case "D":entity.setNegative(value);break;case "E":entity.setStaining(value);break;case "F":entity.setSupportive(value);break;default:break;}}}
}

骚戴理解:String pix = cellReference.substring(0,1);是获取单元格名称的第一个字符,也就是标识列名的字母。在Excel中,单元格名称通常由列名字母和行号组成(例如A1、B2等),这里使用substring方法截取单元格名称的第一个字符来标识列名从而确定对应属性进行赋值操作。

自定义解析

package cn.itcast.poi.test;import cn.itcast.poi.entity.cn.itcast.poi.handler.SheetHandler;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;import java.io.InputStream;
import java.util.Iterator;/*** 使用事件模型解析百万数据excel报表*/
public class PoiTest06 {public static void main(String[] args) throws Exception {String path = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day8\\资源\\百万数据报表\\demo.xlsx";//1.根据excel报表获取OPCPackageOPCPackage opcPackage = OPCPackage.open(path, PackageAccess.READ);//2.创建XSSFReaderXSSFReader reader = new XSSFReader(opcPackage);//3.获取SharedStringTable对象SharedStringsTable table = reader.getSharedStringsTable();//4.获取styleTable对象StylesTable stylesTable = reader.getStylesTable();//5.创建Sax的xmlReader对象XMLReader xmlReader = XMLReaderFactory.createXMLReader();//6.注册事件处理器XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(stylesTable,table,new SheetHandler(),false);xmlReader.setContentHandler(xmlHandler);//7.逐行读取XSSFReader.SheetIterator sheetIterator = (XSSFReader.SheetIterator) reader.getSheetsData();while (sheetIterator.hasNext()) {InputStream stream = sheetIterator.next(); //每一个sheet的流数据InputSource is = new InputSource(stream);xmlReader.parse(is);}}
}

骚戴理解:

  • OPCPackage opcPackage = OPCPackage.open(path, PackageAccess.READ);这行代码的作用是打开并读取一个指定路径的Office Open XML (OOXML)包文件,该文件通常是.xlsx或.xlsm格式的Excel文件。具体来说,OPCPackage类可以从本地文件系统、InputStream或URL中打开OOXML包,并提供了读取、修改和创建OOXML文档的方法。其中,path参数表示要打开的OOXML包文件的路径;PackageAccess.READ表示采用仅读取模式的方式打开该文件。通过OPCPackage对象的实例,Java程序可以访问包中的各种元素,如workbook、sharedStrings等,以及它们的内容,继而进行相应的处理操作。
  • XSSFReader reader = new XSSFReader(opcPackage);这行代码的作用是基于已打开的OOXML包文件,创建一个XSSFReader对象。在Apache POI中,XSSFReader类是用于读取Excel2007及以上版本文件(即.xlsx格式)的类,提供了对工作簿、工作表和单元格等元素的读取操作。reader变量是一个XSSFReader对象的引用,可以通过它访问某个.xlsx文件的所有部件(Parts)并读取其中的数据内容,如工作簿、电子表格、样式集等。具体来说,该代码中的opcPackage参数表示已经打开的.xlsx文件所对应的OPCPackage对象。通过该对象创建XSSFReader对象之后,程序就可以通过调用XSSFReader对象的相应方法,完成Excel文件的解析和读取工作。
  • SharedStringsTable table = reader.getSharedStringsTable();这行代码的作用是通过XSSFReader对象获取Excel文件中的共享字符串表(SharedStringsTable)对象。在Excel2007及以上版本中,相比于早期的.xls格式,.xlsx格式的文件更加节省空间,其中一个原因是采用了共享字符串表来存储重复的字符串值,而非直接将它们写入每个单元格。共享字符串表主要用于存储文本型数据类型,包括文字和数字等。因此,在解析.xlsx格式的文件时,需要先将其包含的共享字符串表加载到内存中,才能通过索引查找对应字符串并读取单元格的值。该代码中的reader参数表示已经创建的XSSFReader对象,程序通过调用其getSharedStringsTable()方法,可以获取Excel文件中的共享字符串表对象,从而实现读取其中存储的字符串值及其索引的功能。得到共享字符串表对象之后,程序可以根据需要使用它的API进行相关的字符串操作,如添加、删除、修改、查询等。
  • StylesTable stylesTable = reader.getStylesTable();这行代码的作用是通过XSSFReader对象获取Excel2007及以上版本文件中的样式表(StylesTable)对象。在Excel文件中,单元格的样式是由一系列属性和特征组成的。通常情况下,如果多个单元格具有相同的样式属性,则可以将它们定义为一个公共的样式引用,并在样式表中记录它们所包含的所有属性信息。样式表主要用于描述单元格、字体、背景色、边框、对齐方式等样式的各种属性和特征。该代码中的reader参数表示已经创建的XSSFReader对象,程序通过调用其getStylesTable()方法,可以获取Excel文件中的样式表对象,从而实现读取其中存储的样式信息的功能。得到样式表对象之后,程序可以根据需要使用它的API对样式进行相关的操作,如添加、修改、删除、查询等。获取样式表对象也是解析Excel2007及以上版本文件的关键步骤之一,因为对单元格的格式化和样式设置直接依赖于样式表。
  • XMLReader xmlReader = XMLReaderFactory.createXMLReader();这行代码的作用是创建一个基于SAX(Simple API for XML)实现的XML解析器对象,该解析器由Java自带的XMLReaderFactory工厂类生成。在Java中,处理XML文档通常有两种方式:DOM和SAX。其中,DOM是一种基于树形结构的解析方式,可以将整个XML文档加载到内存中,并以树形结构表示出来;而SAX则是一种基于事件驱动的解析方式,它会在XML文件解析时产生一系列的事件,我们可以注册回调方法来获取并处理这些事件。由于SAX不需要将XML文档完全加载到内存中,所以对于大型XML文件的处理更加高效和灵活。该代码中使用了Java标准库提供的XMLReaderFactory类生成一个默认配置的SAX解析器对象xmlReader,程序可以通过该对象的相关API实现读取和解析Excel2007及以上版本的xlsx文件。通常情况下,在通过XSSFReader对象获取Excel文件各个部分数据之前,需要先通过该解析器对象将对应部分的XML内容解析为可读格式,再调用相应API进行访问和操作。
 XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(stylesTable,table,new SheetHandler(),false);xmlReader.setContentHandler(xmlHandler);
  • 这两行代码的作用是将读取到的Excel表格数据与对应的样式信息进行关联,并设置XMLReader对象的内容处理器为XSSFSheetXMLHandler对象,进而实现解析.xlsx文件中单个sheet页数据及其样式的功能。其中,参数stylesTable表示Excel文件的样式表对象(StylesTable),参数table表示Excel文件的 共享字符串表对象(SharedStringsTable)。这两个对象是在之前获取Excel数据(包括字符串和样式)时得到的。参数new SheetHandler()表示自定义的SheetHandler对象,该对象继承自DefaultHandler类,实现了XML文档解析时产生的各种事件的回调方法,以便程序能够相应地读取和处理表格中的数据。参数false表示XSSFSheetXMLHandler对象的构造函数不需要启用日期格式化功能,默认为关闭状态。接着,通过xmlReader.setContentHandler(xmlHandler)方法将XSSFSheetXMLHandler对象设置为XMLReader对象的内容处理器。当程序开始解析Excel文件时,XPath表达式会逐一匹配XML文档中的各个节点,当遇到符合条件的节点时,会触发XMLReader对象相应的解析事件,调用XSSFSheetXMLHandler对象对应的回调方法来进行解析、读取和处理。从而实现读取并解析Excel文件中单个表格页数据及其样式的操作。
    XSSFReader.SheetIterator sheetIterator = (XSSFReader.SheetIterator) reader.getSheetsData();while (sheetIterator.hasNext()) {InputStream stream = sheetIterator.next(); //每一个sheet的流数据InputSource is = new InputSource(stream);xmlReader.parse(is);}
  • 这行代码的作用是使用XSSFReader对象获取Excel文件中所有sheet页数据流信息,并封装成SheetIterator迭代器对象,以便程序可以遍历和访问其中的每个sheet页。在Excel文件中,一个.xlsx格式的文件通常包含多个sheet页,每个sheet页由若干行和列构成,其中存储着实际的数据内容。为了读取和处理这些数据内容,需要将各个sheet页数据流信息分别提取出来,并进行相应的解析和操作。该代码中,通过调用reader.getSheetsData()方法得到的SheetIterator对象,可以对sheet页数据流进行逐一迭代,并返回每个sheet页数据的相关信息,如名称、ID、XML路径等。程序可以通过该对象的hasNext()方法检查是否还有下一个sheet页待读取,然后再调用next()方法获取当前sheet页的输入流(InputStream),并对其进行相应的数据读取和解析。在while循环中,每次从迭代器中获取当前sheet页的输入流,并创建一个InputSource对象,以帮助XMLReader对象解析该输入流。然后调用xmlReader.parse(is)方法启动XMLReader对象解析输入流并触发各种回调事件,程序根据解析得到的事件来读取相应的表格数据和样式信息。由于Excel2007及以上版本采用了基于XML标准的文件格式,故在解析时需要借助SAX解析器进行读取。因此,在解析每个sheet页的XML数据之前,需要通过XSSFSheetXMLHandler对象将各个单元格的值与样式进行匹配。

对比测试

用户模式下读取测试Excel文件直接内存溢出,测试Excel文件映射到内存中还是占用了不少内存;事件模式下可以 流畅的运行。

  • 使用用户模型解析

  • 使用事件模型解析

总结

通过简单的分析以及运行两种模式进行比较,可以看到用户模式下使用更简单的代码实现了Excel读取,但是在读 取大文件时CPU和内存都不理想;而事件模式虽然代码写起来比较繁琐,但是在读取大文件时CPU和内存更加占优。

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

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

相关文章

解决react样式组合时css module动态样式失效的问题

现象&#xff1a; <button disabled{invalid} className{ "btn btn-primary btn-lg" invalid ? styles.btnDisabled : "" } > 注册 </button> 上面采用字符串拼接的方式&#xff0c;组合class&#xff0c;但是css module的动态样式style…

Bootstrap的导航菜单组件相关知识

目录 01-最基本的导航示例02-设置导航的对齐方式02-1-设置导航在水平方向上的对齐方式 02-2-设置导航在垂直方向上进行布局03-设计标签页导航04-带下拉菜单的标签页导航05-设计胶囊式导航05-1-基本的胶囊式导航05-2-带下拉菜单的胶囊式导航 06-让导航菜单项可以切换(激活导航菜…

发布npm包质量分测试

查询质量分接口 https://registry.npmjs.org/-/v1/search?textcanvas-plus v0.0.1 quality 0.2987 新建文件夹 canvas-plus 执行命令 npm init 生成package.json {"name": "3r/canvas-plus","version": "0.0.1","descript…

NewStarCTF2023week2-Unserialize?

代码审计&#xff1a; 定义了一个eval类&#xff0c;该类下有一个私有变量cmd和公有成员函数destruct()&#xff0c;该函数在对象的所有引用都被删除或类被销毁时会自动调用&#xff1b; 调用该函数则会执行一个正则表达式进行正则匹配&#xff0c;过滤掉了一些常用命令和bas…

周记之学习总结

你在人群中看到的每一个耀眼的女孩&#xff0c;都是踩着刀尖过来的。你如履平地般地舒适坦然&#xff0c;当然不配拥有任何光芒&#xff1b; 10.11-10.12 思来想去还是不舍得&#xff0c;搞了一下这个jwt&#xff0c;看了很多视频和博客&#xff0c;一直没看懂&#xff0c;两…

没有前端如何测试后端跨域问题

一、问题 前段时间对项目中的跨域做了相关的处理&#xff0c;网上有很多跨域的解决方案。前端解决&#xff0c;后端解决&#xff0c;nginx代理解决。我采用的是在后端中使用Cors来解决跨域的问题。但是前端项目还没有搭建起来&#xff0c;并不知道Cors的解决方案是否会生效&am…

Potato靶机

信息搜集 设备发现 扫描端口 综合扫描 开放了80端口的HTTP服务和7120端口的SSH服务 目录扫描 扫描目录 看看这个info.php&#xff0c;发现只有php的版本信息&#xff0c;没有可以利用的注入点 SSH突破 hydra 爆破 考虑到 7120 端口是 ssh 服务&#xff0c;尝试利用 hydra …

机器学习在工业机器人领域有哪些应用?

随着人工智能和机器学习的快速发展&#xff0c;工业机器人领域也迎来了新的机遇和挑战。本文综述了机器学习在工业机器人领域的应用&#xff0c;包括机器人视觉、运动控制、路径规划、故障诊断等方面。通过对相关研究和实际应用的分析&#xff0c;总结了机器学习在工业机器人领…

【C语言】结构体、位段、枚举、联合(共用体)

结构体 结构&#xff1a;一些值的集合&#xff0c;这些值称为成员变量。结构体的每个成员可以是不同类型的变量&#xff1b; 结构体声明&#xff1a;struct是结构体关键字&#xff0c;结构体声明不能省略struct&#xff1b; 匿名结构体&#xff1a;只能在声明结构体的时候声…

在pycharm中运行js文件,附加node.js下载步骤

文章目录 一、前言二、node.js安装和配置(如果之前就安装好了可以直接跳过)1、进入官网下载安装包2、在本地安装node.js3、环境配置4、验证是否安装成功5、修改下载位置(默认是在c盘&#xff0c;这个根据个人需求)6、设置默认模块包7、测试一下是否修改成功(要进入管理员模式的…

11 | JpaRepository 如何自定义

EntityManager 介绍 Java Persistence API 规定&#xff0c;操作数据库实体必须要通过 EntityManager 进行&#xff0c;而我们前面看到了所有的 Repository 在 JPA 里面的实现类是 SimpleJpaRepository&#xff0c;它在真正操作实体的时候都是调用 EntityManager 里面的方法。…

云上攻防-云原生篇K8s安全Config泄漏Etcd存储Dashboard鉴权Proxy暴露

文章目录 云原生-K8s安全-etcd未授权访问云原生-K8s安全-Dashboard未授权访问云原生-K8s安全-Configfile鉴权文件泄漏云原生-K8s安全-Kubectl Proxy不安全配置 云原生-K8s安全-etcd未授权访问 攻击2379端口&#xff1a;默认通过证书认证&#xff0c;主要存放节点的数据&#x…

升级包版本之后Reflections反射包在springboot jar环境下扫描不到class排查过程记录

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

卡顿分析与布局优化

卡顿分析与布局优化 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能。Android系统每隔大概16.6ms发出VSYNC信 号&#xff0c;触发对UI进行渲染&#xff0c;如果每次渲染都成功&#xff0c;这样就能够达到流畅的画面所需要的60fps&#xff0c;为了能够实现60fp…

LabVIEW生产者消费者架构

LabVIEW生产者消费者架构 生产者/消费者模式可以轻松地同时处理多个进程&#xff0c;同时还能以不同速率迭代。 缓冲通信 当多个进程以不同速度运行时&#xff0c;就适合采用进程间缓冲通信。有了足够大的缓冲区后&#xff0c;生产者循环可以以快于消费者循环的速度运行&…

c语言练习89:链表的使用

链表的使用 虽然有这么多的链表的结构&#xff0c;但是我们实际中最常⽤还是两种结构&#xff1a; 单链表 和 双向带头循环链表 1. ⽆头单向⾮循环链表&#xff1a;结构简单&#xff0c;⼀般不会单独⽤来存数据。实际中更多是作为其他数据结 构的⼦结构&#xff0c;如哈希桶、…

在vs code中创建一个名为 “django_env“ 的虚拟环境报错?!以下方法可以解决

# vs code 终端窗口中运行&#xff1a; mkvirtualenv django_env # 拓展&#xff1a; mkvirtualenv django_env 是一个命令&#xff0c;用于创建一个名为 "django_env" 的虚拟环境。虚拟环境是一种用于隔离不同Python项目所需依赖的工具。通过创建虚拟环境&#x…

word 如何编写4x4矩阵

百度上给的教程&#xff0c;打印出来没有对齐 https://jingyan.baidu.com/article/6b182309995f8dba58e159fc.html 百度上的方式试了一下&#xff0c;不会对齐。导致公式看起来很奇怪。 下面方式会自动对齐 摸索了一下发现可以用下面这种方式编写 4x4 矩阵。先创建一个 3x3…

基于Linux上MySQL8.*版本的安装-参考官网

本地hadoop环境安装好,并安装好mysql mysql下载地址及选择包 MySQL :: Download MyS的QL Community Server (Archived Versions) mysql安装步骤 下载与上传解压给权限 #mysql安装包上传到/opt下 cd /usr/local/ #解压到此目录 tar -xvf /opt/mysql-8.0.33-linux-glibc2.12-…

[Machine Learning][Part 5]监督学习——逻辑回归

之前文章中提到监督学习的应用可分为两类&#xff1a;线性回归和逻辑回归。和线性回归不同&#xff0c;逻辑回归输出只有0和1。对于一个逻辑回归任务&#xff0c;可以先使用线性回归来预测y。然而我们希望逻辑回归预测模型输出的是0和1&#xff0c;为了达到这个目的&#xff0c…