注:本文章中代码均为本地Demo版本,若后续代码更新将不会更新文章
需求说明及实现方式
-
根据从数据库查询出的数据,将其写入excel表并导出
我的想法是通过在实体属性上写自定义注解的方式去完成。因为我们在代码中可以通过反射的方式去获取实体类中全部的注解及属性名称等等。我们可以在自定义注解中声明一个参数
value
,这里面就存储其标题,这样我们 -
数据查询type不同,则显示的标题数量不同
在注解类中增加
type
参数,只有满足对应type
的属性会被导出至excel中 -
数据查询type不同,则显示的标题不同(同一个字段)
优化参数
value
,判断传入的value
是否为json字符串,如果是json字符串则找到其与type
对应的value
-
数据的格式化(时间类型格式化、数据格式化显示)
数据格式化显示通过在注解类中增加
dict
参数,该参数传入json字符串。
本来我是想着通过easyExcel
来完成这些功能,但是由于项目中已经引入了POI的3.9版本依赖,然后easyExcel
中POI的依赖版本又高于该版本,而且不管是版本升级还是版本排除降级,总会有一个出现问题,最终也只能通过最基础的POI编写代码实现。
需求完成
依赖引入:
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.9</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>3.9</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.33</version></dependency>
通用代码
-
ExcelExport
- value:标题,也可为json字符串
- dict:json字符串格式的字典,格式如
User
中所示 - type:数组类型,查询数据type类型是什么值时这个字段会写入excel中。
如我type = {"a"}
,则我在查询数据时传入的type为b则不会将这个字段写入excel中,如果传入的是a则会正常写入。
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface ExcelExport {String value();String dict() default "";String[] type() default {}; }
-
User
- @ExcelExport:即自定义的注解,其中值的含义在上面已经说清楚了
@Data @AllArgsConstructor @NoArgsConstructor @Accessors(chain = true) public class User {@ExcelExport(value = "用户名",type = {"a"})private String userName;@ExcelExport(value = "{a: '年龄',b: '年纪'}",type = {"a","b"})private Integer age;@ExcelExport(value = "性别",dict = "[{ value: \"0\", label: \"女\" }," +"{ value: \"1\", label: \"男\" }]",type = {"a","b"})private Integer sex;@ExcelExport(value = "生日",type = {"b"})private Date birthday; }
版本1
版本1中未实现
数据查询type不同,则显示的标题不同(同一个字段)
这一功能,如需要加请看PoiExcelUtil中writeTitleCellData方法。
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lzj.anno.ExcelExport;
import com.lzj.entity.User;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.util.StringUtils;import java.io.*;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** <p>** </p>** @author:雷子杰* @date:2023/7/4*/
public class One {public static void main(String[] args) throws IOException {List<User> userList = new ArrayList<>();userList.add(new User("lzj1",1,1,new Date()));userList.add(new User("lzj2",2,0,new Date()));userList.add(new User("lzj3",3,1,new Date()));userList.add(new User("lzj4",4,0,new Date()));//声明XSSF对象XSSFWorkbook xssfSheets = new XSSFWorkbook();//创建sheetXSSFSheet userSheet = xssfSheets.createSheet("user");//创建标题字体XSSFFont titleFont = xssfSheets.createFont();titleFont.setBold(true);//加粗titleFont.setFontName("微软雅黑");titleFont.setFontHeightInPoints((short) 12);//字体大小//创建通用字体XSSFFont commonFont = xssfSheets.createFont();commonFont.setBold(false);//加粗commonFont.setFontName("微软雅黑");commonFont.setFontHeightInPoints((short) 12);//字体大小// 创建标题行单元格样式CellStyle titleCellStyle = xssfSheets.createCellStyle();titleCellStyle.setBorderTop(CellStyle.BORDER_THIN);//框线titleCellStyle.setBorderBottom(CellStyle.BORDER_THIN);titleCellStyle.setBorderLeft(CellStyle.BORDER_THIN);titleCellStyle.setBorderRight(CellStyle.BORDER_THIN);titleCellStyle.setAlignment(CellStyle.ALIGN_CENTER);//水平对齐方式titleCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直对齐方式titleCellStyle.setFont(titleFont);//字体样式titleCellStyle.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);//单元格前景色titleCellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//填充单元格//创建通用行单元格样式CellStyle commonCellStyle = xssfSheets.createCellStyle();commonCellStyle.setBorderTop(CellStyle.BORDER_THIN);commonCellStyle.setBorderBottom(CellStyle.BORDER_THIN);commonCellStyle.setBorderLeft(CellStyle.BORDER_THIN);commonCellStyle.setBorderRight(CellStyle.BORDER_THIN);commonCellStyle.setAlignment(CellStyle.ALIGN_CENTER);commonCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);commonCellStyle.setFont(commonFont);commonCellStyle.setWrapText(true);//自动换行//获取实体类中全部属性Field[] fields = User.class.getDeclaredFields();//当前行int currentRow = 0;//当前列int currentColumn = 0;//行高float rowHeight = 40.1f;//列宽int columnWidth = 33 * 256;//创建行XSSFRow row = userSheet.createRow(currentRow);当前行+1//currentRow++;//创建标题行// 遍历每个字段for (Field field : fields) {// 检查字段是否带有Explanation注解if (field.isAnnotationPresent(ExcelExport.class)) {// 获取Explanation注解实例ExcelExport explanation = field.getAnnotation(ExcelExport.class);// 获取注解中的解释String value = explanation.value();//创建单元格,传入值,设置单元格样式XSSFCell cell = row.createCell(currentColumn);cell.setCellValue(value);cell.setCellStyle(titleCellStyle);//设置行高度row.setHeightInPoints(rowHeight);//设置列的宽度userSheet.setColumnWidth(currentColumn,columnWidth);//当前列+1currentColumn++;}}//重置当前列currentColumn = 0;//创建数据行for (User user : userList) {//每次循环时重置列currentColumn = 0;//当前行+1currentRow++;//创建行row = userSheet.createRow(currentRow);for (Field field : fields) {if (field.isAnnotationPresent(ExcelExport.class)) {try {//解除private限制field.setAccessible(true);// 获取Explanation注解实例ExcelExport explanation = field.getAnnotation(ExcelExport.class);// 获取属性的值Object value = field.get(user);//日期类型格式化if (value != null && field.getType() == Date.class){SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");value = sdf.format(value);}//获取对应字典String dict = explanation.dict();if (!StringUtils.isEmpty(dict) && value != null){//JSONObject jsonObject = JSON.parseObject(dict);List<String> list = JSON.parseArray(dict, String.class);for (String item : list) {JSONObject jsonObject = JSON.parseObject(item);if(value == null ? false : jsonObject.getString("value").equals(value.toString()) ){value = jsonObject.getString("label");break;}}//value = jsonObject.get(value.toString());}//创建单元格,传入值,设置单元格样式XSSFCell cell = row.createCell(currentColumn);cell.setCellValue(value == null?"":value.toString());cell.setCellStyle(commonCellStyle);//设置行高度row.setHeightInPoints(rowHeight);//当前列+1currentColumn++;} catch (IllegalAccessException e) {e.printStackTrace();}}}}// 将生成的excel文件输出流转为字节数组byte[] bytes = null;ByteArrayOutputStream outputStream = new ByteArrayOutputStream();xssfSheets.write(outputStream);outputStream.close();bytes = outputStream.toByteArray();//读取字节数组为文件输入流InputStream inputStream = new ByteArrayInputStream(bytes);inputStream.close();//在声明一个输出流将文件下载到本地File file = new File("C:\\Users\\86158\\Desktop\\zzzzzz.xlsx");BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));//将bytes中的内容写入bufferedOutputStream.write(bytes);//刷新输出流,否则不会写出数据bufferedOutputStream.flush();bufferedOutputStream.close();}
}
版本2
版本二相比与版本1,其主要优势是将POI相关操作都封装进了
PoiExcelUtil
中。
-
PoiExcelUtil
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.lzj.anno.ExcelExport; import org.apache.poi.hssf.usermodel.HSSFCellStyle; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.xssf.usermodel.*; import org.springframework.util.StringUtils;import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.*;/*** <p>** </p>** @author:雷子杰* @date:2023/7/6*/ public class PoiExcelUtil {/*** 获取标题字体* @param xssfWorkbook* @return*/public static XSSFFont getTitleFont(XSSFWorkbook xssfWorkbook){//创建标题字体XSSFFont titleFont = xssfWorkbook.createFont();titleFont.setBold(true);//加粗titleFont.setFontName("微软雅黑");titleFont.setFontHeightInPoints((short) 12);//字体大小return titleFont;}/*** 获取通用字体* @param xssfWorkbook* @return*/public static XSSFFont getCommonFont(XSSFWorkbook xssfWorkbook){//创建通用字体XSSFFont commonFont = xssfWorkbook.createFont();commonFont.setBold(false);//加粗commonFont.setFontName("微软雅黑");commonFont.setFontHeightInPoints((short) 12);//字体大小return commonFont;}/*** 获取标题单元格样式* @param xssfWorkbook* @param xssfFont* @return*/public static CellStyle getTitleCellStyle(XSSFWorkbook xssfWorkbook , XSSFFont xssfFont){// 创建标题行单元格样式CellStyle titleCellStyle = xssfWorkbook.createCellStyle();titleCellStyle.setBorderTop(CellStyle.BORDER_THIN);//框线titleCellStyle.setBorderBottom(CellStyle.BORDER_THIN);titleCellStyle.setBorderLeft(CellStyle.BORDER_THIN);titleCellStyle.setBorderRight(CellStyle.BORDER_THIN);titleCellStyle.setAlignment(CellStyle.ALIGN_CENTER);//水平对齐方式titleCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);//垂直对齐方式titleCellStyle.setFont(xssfFont);//字体样式titleCellStyle.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);//单元格前景色titleCellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);//填充单元格return titleCellStyle;}/*** 获取通用单元格样式* @param xssfWorkbook* @param xssfFont* @return*/public static CellStyle getCommonCellStyle(XSSFWorkbook xssfWorkbook, XSSFFont xssfFont){//创建通用行单元格样式CellStyle commonCellStyle = xssfWorkbook.createCellStyle();commonCellStyle.setBorderTop(CellStyle.BORDER_THIN);commonCellStyle.setBorderBottom(CellStyle.BORDER_THIN);commonCellStyle.setBorderLeft(CellStyle.BORDER_THIN);commonCellStyle.setBorderRight(CellStyle.BORDER_THIN);commonCellStyle.setAlignment(CellStyle.ALIGN_CENTER);commonCellStyle.setVerticalAlignment(CellStyle.VERTICAL_CENTER);commonCellStyle.setFont(xssfFont);commonCellStyle.setWrapText(true);//自动换行return commonCellStyle;}/*** 写入单个单元格数据* @param row 行对象* @param xssfSheet sheet对象* @param value 单元格的值* @param cellStyle 单元格样式* @param rowHeight 行高* @param columnWidth 列宽*/public static void writeCellData(XSSFRow row, XSSFSheet xssfSheet , Object value ,CellStyle cellStyle,Integer currentColumn,Float rowHeight,Integer columnWidth){//创建单元格,传入值,设置单元格样式XSSFCell cell = row.createCell(currentColumn);cell.setCellValue(value == null ? "" : value.toString());cell.setCellStyle(cellStyle);//设置行高度row.setHeightInPoints(rowHeight);//设置列的宽度xssfSheet.setColumnWidth(currentColumn,columnWidth);}/**** @param row 行对象* @param xssfSheet sheet对象* @param cellStyle 单元格样式* @param fields 反射获取得到的实体对象的全部属性* @param currentColumn 当前列* @param rowHeight 行高* @param columnWidth 列宽* @param type 类型*/public static void writeTitleCellData(XSSFRow row,XSSFSheet xssfSheet,CellStyle cellStyle,Field[] fields,Integer currentColumn,Float rowHeight,Integer columnWidth,String type){//创建标题行// 遍历每个字段for (Field field : fields) {// 检查字段是否带有ExcelExport注解if (field.isAnnotationPresent(ExcelExport.class)) {// 获取Explanation注解实例ExcelExport explanation = field.getAnnotation(ExcelExport.class);//判断是否是需要写入的数据类型String[] typeArray = explanation.type();Set<String> set = new HashSet<>(Arrays.asList(typeArray));if (!set.contains(type)){continue;}// 获取注解中的解释String value = explanation.value();//判断value是否是json格式数据boolean isJson = true;try{Object parse = JSON.parse(value);}catch (Exception e){isJson = false;}if (isJson == true){//如果是json格式数据,则给他对应对应类型的值JSONObject jsonObject = JSON.parseObject(value);value = jsonObject.getString(type);}//写入单元格数据PoiExcelUtil.writeCellData(row,xssfSheet,value,cellStyle,currentColumn,rowHeight,columnWidth);//当前列+1currentColumn++;}}}/*** 将集合数据全部写入单元格* @param list 需要写入excel的集合数据* @param currentRow 当前行* @param currentColumn 当前列* @param row 行对象* @param xssfSheet sheet对象* @param cellStyle 单元格样式* @param fields 反射获取得到的实体对象的全部属性* @param rowHeight 行高* @param columnWidth 列宽* @param type 类型* @param <T>*/public static <T> void writeCommonRowCellData(List<T> list,Integer currentRow ,Integer currentColumn, XSSFRow row,XSSFSheet xssfSheet,CellStyle cellStyle,Field[] fields,Float rowHeight,Integer columnWidth,String type){//创建数据行for (T obj : list) {//每次循环时重置列currentColumn = 0;//当前行+1currentRow++;//创建行row = xssfSheet.createRow(currentRow);for (Field field : fields) {// 检查字段是否带有ExcelExport注解if (field.isAnnotationPresent(ExcelExport.class)) {try {//解除private限制field.setAccessible(true);// 获取Explanation注解实例ExcelExport explanation = field.getAnnotation(ExcelExport.class);//判断是否是需要写入的数据类型String[] typeArray = explanation.type();Set<String> set = new HashSet<>(Arrays.asList(typeArray));if (!set.contains(type)){continue;}// 获取属性的值Object value = field.get(obj);//日期类型格式化if (value != null && field.getType() == Date.class){SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");value = sdf.format(value);}//获取对应字典String dict = explanation.dict();if (!StringUtils.isEmpty(dict) && value != null){List<String> parseArray = JSON.parseArray(dict, String.class);for (String item : parseArray) {JSONObject jsonObject = JSON.parseObject(item);if(value == null ? false : jsonObject.getString("value").equals(value.toString()) ){value = jsonObject.getString("label");break;}}}//写入单元格数据PoiExcelUtil.writeCellData(row,xssfSheet,value,cellStyle,currentColumn,rowHeight,columnWidth);//当前列+1currentColumn++;} catch (IllegalAccessException e) {e.printStackTrace();}}}}}}
-
Two
import com.lzj.entity.User; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.xssf.usermodel.*;import java.io.*; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Date; import java.util.List;/*** <p>** </p>** @author:雷子杰* @date:2023/7/4*/ public class Two {public static void main(String[] args) throws IOException {List<User> userList = new ArrayList<>();userList.add(new User("lzj1",1,1,new Date()));userList.add(new User("lzj2",2,0,new Date()));userList.add(new User("lzj3",3,1,new Date()));userList.add(new User("lzj4",4,0,new Date()));//声明XSSF对象XSSFWorkbook xssfWorkbook = new XSSFWorkbook();//创建sheetXSSFSheet userSheet = xssfWorkbook.createSheet("user");//创建标题字体XSSFFont titleFont = PoiExcelUtil.getTitleFont(xssfWorkbook);//创建通用字体XSSFFont commonFont = PoiExcelUtil.getCommonFont(xssfWorkbook);// 创建标题行单元格样式CellStyle titleCellStyle = PoiExcelUtil.getTitleCellStyle(xssfWorkbook,titleFont);//创建通用行单元格样式CellStyle commonCellStyle = PoiExcelUtil.getCommonCellStyle(xssfWorkbook,commonFont);//获取实体类中全部属性Field[] fields = User.class.getDeclaredFields();//当前行int currentRow = 0;//当前列int currentColumn = 0;//行高float rowHeight = 40.1f;//列宽int columnWidth = 33 * 256;//创建行XSSFRow row = userSheet.createRow(currentRow);//创建标题行PoiExcelUtil.writeTitleCellData(row,userSheet,titleCellStyle,fields,currentColumn,rowHeight,columnWidth,"b");//重置当前列currentColumn = 0;//创建数据行PoiExcelUtil.writeCommonRowCellData(userList,currentRow,currentColumn,row,userSheet,commonCellStyle,fields,rowHeight,columnWidth,"b");// 将生成的excel文件输出流转为字节数组byte[] bytes = null;ByteArrayOutputStream outputStream = new ByteArrayOutputStream();xssfWorkbook.write(outputStream);outputStream.close();bytes = outputStream.toByteArray();//读取字节数组为文件输入流InputStream inputStream = new ByteArrayInputStream(bytes);inputStream.close();//在声明一个输出流将文件下载到本地File file = new File("C:\\Users\\86158\\Desktop\\zzzzzz.xlsx");BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));//将bytes中的内容写入bufferedOutputStream.write(bytes);//刷新输出流,否则不会写出数据bufferedOutputStream.flush();bufferedOutputStream.close();} }
结果展示
这是我初始化时的数据
下面的是我type参数不同时的数据,均是以版本2来进行的写入导出。type参数修改位置如下:
type参数为a
type参数为b
总结
在项目开发过程中总会遇到各式各样的问题,只有不断的学习,不断的积累,自身水平才能提高。