Java利用注解实现配置动态公式并结合POI导出Excel
利用注解实现动态配置公式并结合POI导出Excel
- 实现思想
- 创建导出对象
- 创建使用注解类
- 创建封装导出对象属性类
- 创建测试类
- 创建ExcelUtil类
- 实现效果
实现思想
实施顾问提出导出Excel数据时,需要带有公式进行计算。目的是方便客户操作,提高客户的工作效率。
所以在提出支持公式导出时,需要我们有基础导出功能。那么需要分析下导出数据的过程:
一、创建导出对象
二、查询导出对象数据
三、利用POI结合导出的数据封装成Excel格式
通过利用上面的三个基础条件,我们在接到该需求时应该如何处理?
一般情况下,可能大部分人通过查询POI相关文档,通过将列进行写死进行设计公式进行实现。如果客户的需求有变动(添加字段,减少字段等)都需要将代码调整一遍。这样费事费力。那么根据以上条件,我们如何设计一个支持配置导出公式的方案呢?
首先,我们想到的是可以在某个地方配置公式,基于这一个思路。可以采用注解的方式,在导出对象的属性上面设置上表达式。那么问题来了?
表达式应该设置成什么格式呢?通过考虑可以采取字段引用的方式描述表达式
//formula 内容为表达式的描述,为了防止在字段上可能有重复,特此使用了特殊符号进行标识,后续为了替换使用
@ExcelColumn(value = "打卡率",formula="@hgzs@/@ydkzs@*100%" )
private Object dkl;
但是,表达式设置好了,如何进行转换为表达式呢?
所以采取了反射的思想,通过导出对象中注解体现的导出顺序,通过ASCII码的方式将获取到Excel对应的列。
然后在导出的时候,将上面的封装的内容进行判断解析,替换成公式。
简单一句话:导出类上描述上表达式,反射获取字段序号,根据序号获取Excel对应的列名,组装数据时将计算的行号和列名进行组装(例如:A1 +B1),在cell上将表达式进行设置上
废话不多说,直接看代码
创建导出对象
import lombok.Data;
import java.io.Serializable;
/*** 导出对象*/
@Data
public class ExcelExportVO implements Serializable {@ExcelColumn(value = "区域市场" )private Object marketName;@ExcelColumn(value = "姓名" )private Object pName;@ExcelColumn(value = "应打卡组数" )private Object ydkzs;@ExcelColumn(value = "打卡签到数" )private Object dkqds;@ExcelColumn(value = "打卡签退数" )private Object dkqts;@ExcelColumn(value = "合格组数" )private Object hgzs;//此处自定义一个类型,处理百分比 合格组数/应打卡组数 * 100%@ExcelColumn(value = "打卡率",formula="@hgzs@/@ydkzs@*100%" )private Object dkl;@ExcelColumn(value = "拜访规划数",formula="@lxbfwcs@+@lxbfwwcs@+@khbfs@+@khbfwwcs@" )private Object bfghs;@ExcelColumn(value = "路线拜访完成数" )private Object lxbfwcs;@ExcelColumn(value = "路线拜访未完成数" )private Object lxbfwwcs;@ExcelColumn(value = "客户拜访完成数" )private Object khbfs;@ExcelColumn(value = "客户拜访未完成数" )private Object khbfwwcs;@ExcelColumn(value = "拜访完成数",formula="@lxbfwcs@+@khbfs@" )private Object bfwcs;@ExcelColumn(value = "拜访完成率",formula="@bfwcs@/@bfghs@*100%" )private Object bfwcl;@ExcelColumn(value = "路线照片有效次数" )private Object lxzpyxcs;@ExcelColumn(value = "路线及客户照片有效次数" ,formula="@lxzpyxcs@+@khbfs@")private Object lxjkhzpyxcs;@ExcelColumn(value = "照片合格率",formula="@lxjkhzpyxcs@/@bfwcs@*100%" )private Object zphgl;@ExcelColumn(value = "临时拜访数" )private Object lsbfs;@ExcelColumn(value = "临时照片有效次数")private Object lszpyscs;@ExcelColumn(value = "是否规划线路" )private Object sfghxl;@ExcelColumn(value = "周综合完成率" )private Object zzhwcl;}
创建使用注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 列表需要展示的注解,需要带上此注解,目的是有动态列,并且列表展示哪个就要对哪个加入相关的注解*/
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumn {/*** 名称* @return*/public String value() default "";/*** 计算公式:能不能采用jpel表达式搞*/public String formula() default "";}
创建封装导出对象属性类
import lombok.Getter;
import lombok.Setter;import java.io.Serializable;/*** Excel单元格字段样式描述*/
@Getter
@Setter
public class ExcelCellStyleVO implements Serializable {/*** 对应的key值*/private String key;/*** 对应的内容*/private String value;/*** 类型*/private String type;/*** 表达式*/private String formula;/*** 列名*/private String columnName;}
创建测试类
public class ExcelExportNewTest {public static void main(String[] args) throws IOException {int count = 400;JSONArray studentArray = new JSONArray();for (int i = 1; i <= count; i++) {ExcelExportVO s = new ExcelExportVO();s.setMarketName("区域"+i); s.setPName("小米"+i);s.setYdkzs(i);s.setDkqds(i);s.setDkqts(i);s.setHgzs(i);
// s.setDkl();
// s.setBfghs();s.setLxbfwcs(i);s.setLxbfwwcs(i);s.setKhbfs(i);s.setKhbfwwcs(i);s.setBfwcs(i);
// s.setBfwcl();s.setLxzpyxcs(i);s.setLxjkhzpyxcs(i);
// s.setZphgl(i);s.setLsbfs(i);s.setLszpyscs(i);s.setSfghxl(1);s.setZzhwcl(0);studentArray.add(s);}/** titleList存放了2个元素,分别为titleMap和headMap*/// 1.titleMap存放了该excel的头信息LinkedHashMap<String, String> titleMap = new LinkedHashMap<String, String>();titleMap.put("title1", "sheet1");// 2.headMap存放了该excel的列项// titleList.add(titleMap);LinkedHashMap<String, ExcelCellStyleVO > map = getTitleMap(ExcelExportVO.class);File file = new File("D://ExcelExportDemo/");if (!file.exists()) file.mkdirs();// 创建该文件夹目录OutputStream os = null;try {System.out.println("正在导出xlsx...");long start = System.currentTimeMillis();// .xlsx格式os = new FileOutputStream(file.getAbsolutePath() + File.separator + start + ".xlsx");ExcelNewUtil.exportExcel(titleMap,map, studentArray, ExcelExportVO.class,os);System.out.println("导出完成...共" + count + "条数据,用时" + (System.currentTimeMillis() - start) + "毫秒");System.out.println("文件路径:" + file.getAbsolutePath() + File.separator + start + ".xlsx");} catch (Exception e) {e.printStackTrace();} finally {os.close();}}private static LinkedHashMap<String, ExcelCellStyleVO > getTitleMap(Class clazz){LinkedHashMap<String, ExcelCellStyleVO> map = new LinkedHashMap<>();Field[] fields = clazz.getDeclaredFields();ExcelCellStyleVO vo = null;Map<String, String> columnNameMap = new HashMap<>();int i =1;for(Field field : fields) {field.setAccessible(true);String fieldName = field.getName();Annotation annotation = field.getAnnotation(ExcelColumn.class);if (annotation instanceof ExcelColumn) {columnNameMap.put( fieldName,ExcelNewUtil.getColumnName(i) ) ;}i++;}for(Field field : fields){//属性field.setAccessible(true);String fieldName = field.getName();Annotation annotation = field.getAnnotation(ExcelColumn.class);if (annotation instanceof ExcelColumn) {ExcelColumn excelColumn = (ExcelColumn) annotation;vo =new ExcelCellStyleVO();vo.setKey(fieldName);vo.setValue(excelColumn.value());String formula = excelColumn.formula();if(StringUtils.isNotBlank(formula)){//将表达式内的公式进行替换,并且带有好对应的下标才行for (Map.Entry<String, String> entry : columnNameMap.entrySet()) {String key = "@"+entry.getKey()+"@";if(formula.contains(key) ){formula = formula.replace(key,entry.getValue()+"@num@");}}vo.setFormula(formula); //其实此时需要将表达式进行处理才行,拼装成对应的公式 即 SUM A1,A2}//此时知道当前字段在那一列上vo.setColumnName(columnNameMap.get(fieldName));map.put(fieldName,vo);}}return map;}
}
创建ExcelUtil类
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yonyou.ocm.common.service.dto.BaseDto;
import oracle.net.aso.i;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFDataValidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;public class ExcelNewUtil <T> {public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";// 默认日期格式public static final int DEFAULT_COLUMN_WIDTH = 17;// 默认列宽//校验用private static String CONTAIN_LETTER_REGEX = ".*[a-zA-z].*";private static Logger logger = LoggerFactory.getLogger(ExcelNewUtil.class);/*** 导出Excel(.xlsx)格式* @param titleList 表格头信息集合* @param dataArray 数据数组* @param os 文件输出流*/public static void exportExcel(LinkedHashMap<String, String> titleMap, LinkedHashMap<String, ExcelCellStyleVO> titleList, JSONArray dataArray, Class clazz, OutputStream os) {int minBytes = DEFAULT_COLUMN_WIDTH;/*** 声明一个工作薄*/SXSSFWorkbook workbook = new SXSSFWorkbook(1000);// 大于1000行时会把之前的行写入硬盘workbook.setCompressTempFiles(true);// head样式CellStyle headerStyle = workbook.createCellStyle();headerStyle.setAlignment(HorizontalAlignment.CENTER);headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);headerStyle.setFillForegroundColor(HSSFColor.LIGHT_GREEN.index);// 设置颜色headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);// 前景色纯色填充headerStyle.setBorderTop(BorderStyle.THIN);headerStyle.setBorderRight(BorderStyle.THIN);headerStyle.setBorderBottom(BorderStyle.THIN);headerStyle.setBorderLeft(BorderStyle.THIN);Font headerFont = workbook.createFont();headerFont.setFontHeightInPoints((short) 12);headerFont.setBold(true);//加粗headerStyle.setFont(headerFont);// 单元格样式CellStyle cellStyle = workbook.createCellStyle();cellStyle.setAlignment(HorizontalAlignment.CENTER);cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);cellStyle.setBorderTop(BorderStyle.THIN);cellStyle.setBorderRight(BorderStyle.THIN);cellStyle.setBorderBottom(BorderStyle.THIN);cellStyle.setBorderLeft(BorderStyle.THIN);Font cellFont = workbook.createFont();cellFont.setBold(false);//加粗cellStyle.setFont(cellFont);XSSFDataFormat dataFormat = (XSSFDataFormat) workbook.createDataFormat();CellStyle intStyle = workbook.createCellStyle();intStyle.setAlignment(HorizontalAlignment.CENTER);intStyle.setVerticalAlignment(VerticalAlignment.CENTER);intStyle.setBorderTop(BorderStyle.THIN);intStyle.setBorderRight(BorderStyle.THIN);intStyle.setBorderBottom(BorderStyle.THIN);intStyle.setBorderLeft(BorderStyle.THIN);intStyle.setFont(cellFont);intStyle.setDataFormat(dataFormat.getFormat("#,#0"));CellStyle baifenStyle = workbook.createCellStyle();baifenStyle.setDataFormat(dataFormat.getFormat("0.00%"));String title1 = (String) titleMap.get("title1");LinkedHashMap<String, ExcelCellStyleVO > headMap = titleList;/*** 生成一个(带名称)表格*/SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet(title1);sheet.createFreezePane(0, 1, 0, 0);// (单独)冻结前三行/*** 生成head相关信息+设置每列宽度*/int[] colWidthArr = new int[headMap.size()];// 列宽数组String[] headKeyArr = new String[headMap.size()];// headKey数组String[] headValArr = new String[headMap.size()];// headVal数组int i = 0;for (Map.Entry<String, ExcelCellStyleVO> entry : headMap.entrySet()) {headKeyArr[i] = entry.getKey();ExcelCellStyleVO styleVO = entry.getValue();headValArr[i] = styleVO.getValue();int bytes = headKeyArr[i].getBytes().length;colWidthArr[i] = bytes < minBytes ? minBytes : bytes;sheet.setColumnWidth(i, colWidthArr[i] * 256);// 设置列宽i++;}/*** 遍历数据集合,产生Excel行数据*/int rowIndex = 0;for (Object obj : dataArray) {// 生成title+head信息if (rowIndex == 0) {SXSSFRow headerRow = (SXSSFRow) sheet.createRow(0);// head行for (int j = 0; j < headValArr.length; j++) {headerRow.createCell(j).setCellValue(headValArr[j]);headerRow.getCell(j).setCellStyle(headerStyle);}rowIndex = 1;}JSONObject jo = (JSONObject) JSONObject.toJSON(obj);// 生成数据SXSSFRow dataRow = (SXSSFRow) sheet.createRow(rowIndex);// 创建行for (int k = 0; k < headKeyArr.length; k++) {SXSSFCell cell = (SXSSFCell) dataRow.createCell(k);// 创建单元格String curKey = headKeyArr[k];Object o = jo.get(curKey);if(o != null ){if (o instanceof Integer) {cell.setCellValue(new BigDecimal(o.toString().trim()).doubleValue());} else {cell.setCellValue(o.toString());}}else{cell.setCellValue("");}if(rowIndex != 0 && headMap.containsKey(curKey)){ExcelCellStyleVO vo = headMap.get(curKey);if(vo != null && StringUtils.isNotBlank( vo.getFormula())){//此时需要进行替换公式 @+++@ 直接就是替换哪一行String formula = vo.getFormula();formula = formula.replace("@num@",(rowIndex+1)+"");cell.setCellFormula(formula);if(formula.contains("100%")){cell.setCellStyle(baifenStyle);}}}}rowIndex++;}//我们还需要在写进文件中之前对导入的内容进行设置,让其在打开Excel之前,要求Excel重新计算改工作簿中的所有公式sheet.setForceFormulaRecalculation(true);try {workbook.write(os);os.flush();// 刷新此输出流并强制将所有缓冲的输出字节写出os.close();// 关闭流workbook.dispose();// 释放workbook所占用的所有windows资源} catch (IOException e) {e.printStackTrace();}}/*** 根据索引列获取真实的Excel列【A B C ...】* @param columnNum* @return*/public static String getColumnName(int columnNum) {int first;int last;String result = "";if (columnNum > 256)columnNum = 256;first = columnNum / 27;last = columnNum - (first * 26);if (first > 0)result = String.valueOf((char) (first + 64));if (last > 0)result = result + String.valueOf((char) (last + 64));return result;}}
实现效果
Java利用注解实现配置动态公式并结合POI导出Excel相关推荐
- JAVA导出Excel通用工具类——第一篇:详细介绍POI 导出excel的多种复杂情况,包括动态设置筛选、动态合并横向(纵向)单元格等多种复杂情况——保姆级别,真的不能再详细了,代码拿来即用)
JAVA导出Excel通用工具--第一篇:详细介绍POI 导出excel的多种复杂情况,包括动态设置筛选.动态合并横向(纵向)单元格等多种复杂情况--保姆级别,真的不能再详细了,封装通用工具类,代码拿 ...
- java利用poi导出excel功能-附带图片导出
java利用poi导出excel功能-附带图片导出 写在前面 最近刚离职,闲来无事,于是把上两家公司都有碰到过的需求但都没有去研究实现:即导出带图片的excel报表.于是就折腾了一下这个功能,研究出来 ...
- Java web项目利用POI导出EXCEL表格
SSH2 POI导出EXCEL表格 1.首先导入poi的jar包 HSSFWorkbook :工作簿,代表一个excel的整个文档 HSSFSheet:工作表 HSSFRow :行 HSSFCell: ...
- Java POI 导出EXCEL经典实现 Java导出Excel弹出下载框
原文转载:http://blog.csdn.net/evangel_z/article/details/7332535 目录(?)[+] 在web开发中,有一个经典的功能,就是数据的导入导出.特别是数 ...
- Java POI 导出EXCEL经典实现 Java导出Excel
转自http://blog.csdn.net/evangel_z/article/details/7332535 在web开发中,有一个经典的功能,就是数据的导入导出.特别是数据的导出,在生产管理或者 ...
- Java和poi导出excel报表
一:poi jar下载地址:点击打开链接: 二:工程截图: 三:运行效果截图: 四:源代码: Student.java: package com.poi.bean;import java.util.D ...
- cpu java poi 导出_java基于poi导出excel透视表代码实例
这篇文章主要介绍了java基于poi导出excel透视表代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从前,我是一个前端程序猿,怀着对打通 ...
- @excel注解_java bean 一对多,多对一 poi导出excel表格
最近造了个poi导出 excel轮子 特点 java bean 一对多.多对一关系合并单元行 支持图片导出 Bean 一对多关系合并行 代码 public class User { @Excel(na ...
- Springboot利用poi导出excel下载
Springboot利用poi导出excel下载 因为项目中之前的做法是用反射获取属性,所以demo中也是用的反射,我看网上很多文章都是存入一个List中,不知道这两种哪种更何合适一点,或者有什么更好 ...
最新文章
- 在线作图|在线做UMAP降维分析
- Xcode 添加前缀
- linux gcc编译c文件头文件,使用GCC编译器编译C语言
- ant警告 “warning: 'includeantruntime' was not set”解决方法
- nslookup 包含在那个包中_nslookup命令详解
- Android 事件处理
- Silverlight4.0教程之WebBrowser控件(Silverlight内置HTML浏览器控件)
- 小程序和android联调,小程序打开APP指定页面
- c语言技术课第一次作业
- springboot 网页聊天室
- Java继承中的覆盖
- 为什么普通人做量化交易会亏钱?
- DataMatrix 数据容量
- 基于Android的天气预报系统的设计和实现
- 求两个数中的最大值最小值算法
- html在线聊天界面模板,一款带气泡对话框的HTML5聊天应用界面模板
- Pytorch 叶子张量 leaf tensor (叶子节点) (detach)
- Intellij IDEA的安装及使用介绍
- (转)李开复的美东AI见闻
- 在vue中使用flexible响应式布局——默认html字体大小(font-size)是54px的问题