1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 自定义注解+POI实现流式数据导入 支持各数据类型转换

自定义注解+POI实现流式数据导入 支持各数据类型转换

时间:2019-09-18 18:15:59

相关推荐

自定义注解+POI实现流式数据导入 支持各数据类型转换

😊 @ 作者: 一恍过去 💖 @ 主页: /zhuocailing3390 🎊 @ 社区: Java技术栈交流 🎉 @ 主题: 自定义注解+POI实现流式数据导入、导出功能 ⏱️ @ 创作时间: 04月01日

目录

前言1、概述2、POM引入3、自定义注解4、实体类5、数据转换类6、导入处理类7、测试接口

前言

POI实现流式数据导入是将实时产生的POI数据从源传输到目标系统的过程,需要考虑数据源接入、数据传输、数据转换与处理、实时导入以及监控和故障处理等步骤。

要实现POI的流式数据导入,通常需要考虑以下几个步骤:

数据源接入:首先,需要确定数据的来源。这可以是实时生成的POI数据,例如移动应用程序或传感器产生的数据,也可以是来自第三方提供的数据源,如实时位置服务提供商的数据流。数据传输:一旦数据源确定,需要建立相应的数据传输机制来将数据从源发送到目标系统。这可以通过使用消息队列、流处理引擎或其他实时数据传输技术来实现。数据转换与处理:在将数据导入目标系统之前,可能需要对数据进行转换和处理。这可能涉及数据清洗、格式转换、数据结构调整等操作,以适应目标系统的要求。实时导入:在数据准备就绪后,可以使用流式处理引擎或自定义的数据处理逻辑,将数据实时导入到目标系统中。这可以涉及数据持久化、索引建立、数据更新等操作,以便后续的查询和分析。监控和故障处理:流式数据导入过程中,需要监控导入任务的状态和性能,并及时处理任何可能的故障或错误。这可以包括监控数据传输的稳定性、处理延迟以及数据一致性等方面。

1、概述

使用POI工具包,可以在Java中读取、创建和编辑这些不同类型的Excel文件。你可以读取现有的文件内容、创建新文件、修改内容、添加样式和格式,并将修改后的文件保存到本地或通过网络进行传输。

本文使用POI工具包,配合自定义注解+反射机制,实现Excel数据的导入;并且导入操作时,通过流式处理实现解析Excel数据的同时对数据进行处理,不需要等所有excel解析完成后再对数据操作,提高效率。

2、POM引入

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency><!-- 操作xlsx格式的excel文件 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-excelant</artifactId><version>4.1.2</version></dependency>

3、自定义注解

通过自定义注解,可以实现在导入时,只有指定被注解修饰的字段才会进行解析,并且解析时可以根据注解值实现格式化等数据操作。

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** 自定义导出Excel数据注解*/@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Excel {/*** 导出表头名字.*/String name() default "";/*** 日期格式, 如: yyyy-MM-dd*/String dateFormat() default "";/*** 数值映射处理 (如: 0-男,1-女)*/String convertValue() default "";/*** BigDecimal、double保留小数位,默认2位*/int scale() default 2;/*** 对导出的数据追加后缀文本*/String suffix() default "";/*** 导出字段对齐方式(0:默认;1:靠左;2:居中;3:靠右)*/int align() default 0;}

4、实体类

@Datapublic class User {@Excel(name = "姓名")public String name;@Excel(name = "年龄")public Integer age;@Excel(name = "性别", convertValue = "0-男,1-女")public Integer gender;@Excel(name = "体重", scale = 3)public Double weight;@Excel(name = "体重", scale = 4)public BigDecimal height;@Excel(name = "入学时间", dateFormat = "yyyy-MM-dd HH:mm:ss")public Date joinTime;@Excel(name = "入学日期", dateFormat = "yyyy-MM-dd")public LocalDateTime joinDate;}

5、数据转换类

封装公共的导入数据处理工具类,以便于在其他请求方法中,可以直接进行使用,同时也提高了代码的可维护性、重用性和安全性

package cn.lihuazhi.poi.util;import java.lang.reflect.Field;import java.math.BigDecimal;import java.math.RoundingMode;import java.text.DecimalFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.Date;/*** @Date: /4/1 10:34* @Description: excel与实体类, 数据类型的转换**/public class ConvertValueUtil {/*** =========== 导入相关处理开始 ===========*/public static String convertValue(String convertValue, Object value) {// 比如 :0-男,1-女 或者 man-男,woman-女String[] split1 = convertValue.split(",");for (String val : split1) {String[] split2 = val.split("-");if (split2[1].equals(value)) {value = split2[0];}}return value.toString();}public static String dateFormat(String dateFormat, Object value) {if (value instanceof Date) {value = ConvertValueUtil.dateToString(dateFormat, (Date) value);}if (value instanceof LocalDateTime) {value = ConvertValueUtil.localDateTimeToString(dateFormat, (LocalDateTime) value);}return value.toString();}public static Date stringToDate(String dateFormat, String dateValueStr) {try {dateFormat = dateFormat == null ? "yyyy-MM-dd HH:mm:ss" : dateFormat;return new SimpleDateFormat(dateFormat).parse(dateValueStr);} catch (ParseException e) {e.printStackTrace();}return null;}public static LocalDateTime stringToLocalDateTime(String dateValueStr) {String dateFormat = "yyyy-MM-dd HH:mm:ss";DateTimeFormatter df = DateTimeFormatter.ofPattern(dateFormat);return LocalDateTime.parse(dateValueStr, df);}public static String dateToString(String dateFormat, Date value) {return new SimpleDateFormat(dateFormat).format(value);}public static String localDateTimeToString(String dateFormat, LocalDateTime value) {return DateTimeFormatter.ofPattern(dateFormat).format(value);}public static Date convertToDate(String dateFormat, Object value) {Date dateValue = null;String dateValueStr = null;try {if (value instanceof String) {dateValueStr = value.toString();}if (value instanceof Date) {dateValueStr = ConvertValueUtil.dateToString(dateFormat, (Date) value);}if (value instanceof LocalDateTime) {dateValueStr = ConvertValueUtil.localDateTimeToString(dateFormat, (LocalDateTime) value);}if (dateValueStr == null) {return null;}dateValue = stringToDate(dateFormat, dateValueStr);} catch (Exception e) {System.out.println("数据转换异常");}return dateValue;}public static LocalDateTime convertToLocalDateTime(Object value) {// LocalDateTime在格式化时,只支持"yyyy-MM-dd HH:mm:ss"类型LocalDateTime dateValue = null;String dateValueStr = null;try {if (value instanceof String) {dateValueStr = value.toString();}if (value instanceof Date) {dateValueStr = ConvertValueUtil.dateToString("yyyy-MM-dd HH:mm:ss", (Date) value);}if (value instanceof LocalDateTime) {dateValueStr = ConvertValueUtil.localDateTimeToString("yyyy-MM-dd HH:mm:ss", (LocalDateTime) value);}if (dateValueStr == null) {return null;}dateValue = stringToLocalDateTime(dateValueStr);} catch (Exception e) {System.out.println("数据转换异常");}return dateValue;}public static Boolean convertToBool(Object value) {if (value instanceof Boolean) {return (Boolean) value;}if (value instanceof String) {String val = value.toString();switch (val) {case "true":case "是":return true;case "false":case "否":return false;default:return null;}}return null;}public static Integer convertToInt(Object value) {Integer intValue = null;if (value instanceof Integer) {intValue = (Integer) value;}if (value instanceof Number) {intValue = ((Number) value).intValue();}if (value instanceof String) {intValue = Integer.parseInt(value.toString().trim());}return intValue;}public static Long convertToLong(Object value) {Long longValue = null;if (value instanceof Long) {longValue = (Long) value;}if (value instanceof Number) {longValue = ((Number) value).longValue();}if (value instanceof String) {longValue = Long.parseLong(value.toString().trim());}return longValue;}public static Float convertToFloat(int scale, Object value) {Float floatValue = null;if (value instanceof Float) {floatValue = (Float) value;}if (value instanceof Number) {floatValue = ((Number) value).floatValue();}if (value instanceof String) {floatValue = Float.parseFloat(value.toString().trim());}if (scale > 0 && floatValue != null) {floatValue = Float.parseFloat(decimalFormat(scale, floatValue));}return floatValue;}public static Double convertToDouble(int scale, Object value) {Double doubleValue = null;if (value instanceof Double) {doubleValue = (Double) value;}if (value instanceof Number) {doubleValue = ((Number) value).doubleValue();}if (value instanceof String) {doubleValue = Double.parseDouble(value.toString().trim());}if (scale > 0 && doubleValue != null) {doubleValue = Double.parseDouble(decimalFormat(scale, doubleValue));}return doubleValue;}public static BigDecimal convertToBigDecimal(int scale, Object value) {BigDecimal bigDecimal = null;if (value instanceof BigDecimal) {bigDecimal = (BigDecimal) value;}if (value instanceof Number) {Double convertToDouble = convertToDouble(scale, value);bigDecimal = new BigDecimal(convertToDouble);}if (value instanceof String) {bigDecimal = new BigDecimal(value.toString().trim());}if (scale > 0 && bigDecimal != null) {bigDecimal = bigDecimal.setScale(scale, RoundingMode.HALF_UP);}return bigDecimal;}private static String decimalFormat(int scale, Object value) {StringBuilder scaleStr = new StringBuilder();for (int index = 1; index <= scale - 2; index++) {scaleStr.append("0");}String pattern = scale > 2 ? "#0.00" + scaleStr : "#0.00";DecimalFormat df = new DecimalFormat(pattern);return df.format(value);}/*** =========== 导入相关处理结束 ===========*/}

6、导入处理类

实现对导入数据的解析以及根据注解字段,进行数据处理操作。

import cn.lihuazhi.poi.util.ConvertValueUtil;import org.apache.poi.ss.usermodel.*;import org.springframework.util.StringUtils;import java.io.InputStream;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.math.BigDecimal;import java.time.LocalDateTime;import java.util.*;import java.util.function.Consumer;/*** @Date: /1/16 14:58* @Description:**/public class ExcelImportHandler<T> {/*** 批量处理梳理*/public Integer batchNum;/*** 封装的entity对象*/public Class<?> entity;public Constructor<?> constructor;/*** 传入解析数据的service对象*/public Consumer<List<T>> uploadService;/*** 接收解析对象值*/public List<T> list = new ArrayList<>();public ExcelImportHandler(Consumer<List<T>> uploadService, Class<?> entity, Integer batchNum) {this.uploadService = uploadService;this.entity = entity;this.batchNum = batchNum == null ? 1000 : batchNum;}/*** @param inputStream 数据流* @throws Exception*/public void handlerData(InputStream inputStream) throws Exception {// 创建构造器this.constructor = entity.getDeclaredConstructor();// 创建表格对象Workbook workbook = WorkbookFactory.create(inputStream);//有多少个sheetint sheets = workbook.getNumberOfSheets();for (int i = 0; i < sheets; i++) {Sheet sheet = workbook.getSheetAt(i);//获取多少行int rows = sheet.getPhysicalNumberOfRows();//注意:第0为表头,第1行开始解析数据,处理中文表头与字段的映射关系Row heard = sheet.getRow(0);Map<Integer, Field> cellHeadMap = getCellHeadMap(heard);// 解析表格数据for (int currentRow = 1; currentRow < rows; currentRow++) {parseCellData(sheet, currentRow, cellHeadMap);}}// 所有数据解析完成后saveData(list);}private Map<Integer, Field> getCellHeadMap(Row heard) {// 定义一个map用于存放excel列的序号和field.Map<Integer, Field> cellMap = new HashMap<>(8);// 有数据时才处理 得到类的所有field.Field[] allFields = entity.getDeclaredFields();for (Field field : allFields) {Excel excel = field.getAnnotation(Excel.class);if (excel == null) {continue;}field.setAccessible(true);for (int column = 0; column < heard.getPhysicalNumberOfCells(); column++) {Cell cell = heard.getCell(column);if (cell != null && cell.getStringCellValue() != null && excel.name().equals(cell.getStringCellValue().trim())) {// 表头的表格名称cellMap.put(column, field);break;}}}return cellMap;}/*** 获取表格数据** @param sheet* @param currentRow* @return*/private void parseCellData(Sheet sheet, int currentRow, Map<Integer, Field> cellHeadMap) throws Exception {//获得第行号Row row = sheet.getRow(currentRow);// 解析正文数据T instance = (T) entity.newInstance();for (int cellNum = 0; cellNum < row.getLastCellNum(); cellNum++) {Field field = cellHeadMap.get(cellNum);if (field == null) {continue;}field.setAccessible(true);Cell cell = row.getCell(cellNum);// 获取excel中的数据Object value = getCellValue(cell);// 根据注解对数据做转换value = convertValue(value, field);field.set(instance, value);}list.add(instance);if (list.size() >= batchNum) {// 回调接口,处理数据saveData(list);}}private Object convertValue(Object value, Field field) {Class<?> type = field.getType();if (type == String.class) {// Excel数据存在日期格式化String dateFormat = field.getAnnotation(Excel.class).dateFormat();// 映射处理String convertValue = field.getAnnotation(Excel.class).convertValue();// 文本追加String suffix = field.getAnnotation(Excel.class).suffix();if (!StringUtils.isEmpty(dateFormat)) {value = ConvertValueUtil.dateFormat(dateFormat, value);} else if (!StringUtils.isEmpty(convertValue)) {value = ConvertValueUtil.convertValue(convertValue, value);} else if (!StringUtils.isEmpty(suffix)) {value = value.toString() + suffix;}}if (type == Integer.class) {// 映射处理String convertValue = field.getAnnotation(Excel.class).convertValue();if (!StringUtils.isEmpty(convertValue)) {value = ConvertValueUtil.convertValue(convertValue, value);}value = ConvertValueUtil.convertToInt(value);}if (type == Long.class) {// 映射处理String convertValue = field.getAnnotation(Excel.class).convertValue();if (!StringUtils.isEmpty(convertValue)) {value = ConvertValueUtil.convertValue(convertValue, value);} else {value = ConvertValueUtil.convertToLong(value);}}if (type == Double.class) {int scale = field.getAnnotation(Excel.class).scale();value = ConvertValueUtil.convertToDouble(scale, value);}if (type == Float.class) {int scale = field.getAnnotation(Excel.class).scale();value = ConvertValueUtil.convertToFloat(scale, value);}if (type == BigDecimal.class) {int scale = field.getAnnotation(Excel.class).scale();value = ConvertValueUtil.convertToBigDecimal(scale, value);}if (type == Boolean.class) {value = ConvertValueUtil.convertToBool(value);}if (type == Date.class) {String dateFormat = field.getAnnotation(Excel.class).dateFormat();value = ConvertValueUtil.convertToDate(dateFormat, value);}if (type == LocalDateTime.class) {value = ConvertValueUtil.convertToLocalDateTime(value);}return value;}/*** 解析数据类型** @param cell* @return*/private Object getCellValue(Cell cell) {//1.获取到单元格的属性类型CellType cellType = cell.getCellType();//2.根据单元格数据类型获取数据Object value = null;switch (cellType) {case STRING:value = cell.getStringCellValue();break;case BOOLEAN:value = cell.getBooleanCellValue();break;case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {//日期格式value = cell.getDateCellValue();} else {//数字,poi解析的数值都是double类型value = cell.getNumericCellValue();}break;case FORMULA://公式value = cell.getCellFormula();break;default:break;}return value;}/*** 回调保存数据** @param dataList*/private void saveData(List<T> dataList) {if (dataList.size() > 0) {uploadService.accept(dataList);dataList.clear();}}}

7、测试接口

import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.util.List;import java.util.function.Consumer;@RestController@RequestMapping("/poi")public class PoiExcelController {@Resourceprivate HttpServletResponse response;/*** 数据导入** @param file* @return*/@PostMapping("importExcel")@ResponseBodypublic String importExcel(@RequestParam("file") MultipartFile file) throws Exception {try {if (file == null) {return "文件为空";}InputStream inputStream = file.getInputStream();// 注册事件处理器Consumer<List<User>> consumer = this::importDataEnd;// 传入参数进行解析ExcelImportHandler importHandler = new ExcelImportHandler(consumer, User.class, 100);importHandler.handlerData(inputStream);} catch (IOException e) {e.printStackTrace();return "解析失败";}return "操作成功";}/*** 每批量解析一批数据则执行该方法** @param list*/public void importDataEnd(List<User> list) {System.out.println("解析数量:" + list.size());System.out.println("解析数据:" + list);}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。