JAVA 之POI导入批量新增、批量检查、日志记录、失败原因、失败条数、数据库映射

前言

欢迎大家来到我的博客,请各位看客们点赞、收藏、关注三连!

欢迎大家关注我的知识库,Java之从零开始·语雀

你的关注就是我前进的动力!

CSDN专注于问题解决的博客记录,语雀专注于知识的收集与汇总,包括分享前沿技术。

需求分析

Excel导入去做一些前置检查,包括非空检查根据前面的字段生成对应的值唯一索引等检查。
以及导入的数据失败的原因,成功的条数日志记录。
Excel的标题名和字段名数据库映射。

思路

数据映射

之前其实是有一个版本,是按照getCell(0) 列数来获取的,并没有做数据映射。这个有一个问题,就是模板一旦出现一点改动,随之而来的就是代码的大量改动。

并且硬编码代码过多,当然他也有优点,效率高。

为了完成数据映射,那么就必须引入反射,自定义注解来帮助我们完成数据映射,自定义注解引入的话,就会出现一个bean需要单独的维护。

字典

并且之前的字典写法,static {
// 加载map
}

创建了n个map,然后put进去。

这样子,会影响性能。

下个一版本,改为enum枚举类。
在这里插入图片描述

有效行数

还有一个成功条数和失败条数,本人使用了Atomic原子类,总觉得i++,i–不放心,所以我就使用了原子类。 POI的API没有计算有效行数,故而需要我们写代码实现。比如:getLastRowNum()方法获取也不是正确的行数。

什么是有效行数?

每一行有真正的数据,而不是空行数据。
在这里插入图片描述

这个逻辑就是有三个原子类。
在这里插入图片描述
我认为当连续空行数大于100行,那么没必要继续遍历查询。

计算有内容行核心逻辑

在这里插入图片描述

难点与问题

第一个难点

list.add()内容覆盖问题
参照我的博客解决方案。
https://blog.csdn.net/qq_41520636/article/details/124493078

第二个难点

比如:
class A {
private B b;
}

class B {

}

此时应该怎么处理这个B,相当于标题栏里面正好有两个Bean,还好这个情况只有一个,不然我心态崩了。
这个解决方案只有看代码了。

第三个难点

比如:
class A{
private String name;
}

class B {
private String name;
}
这两个其实一对一关系,并且在标题映射时,我用怎么让B获取到A的值。

实现

因为是公司代码就不贴全部代码,只贴核心代码。

/**
 * <p>
 *    excel导入注解
 * </p>
 *
 * @author lisonglin
 * @version 1.0
 * @since 2022/4/25 16:19
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelImport {

	/**
	 * 与excel标题头对应
	 */
	String exportName();

	/**
	 * 转换格式, 如时间类型 yyyy-MM-dd HH:mm:ss
	 */
	String pattern() default "";

	/**
	 * 在excel中位置
	 */
	int order() default 0;

	/**
	 * 长度
	 **/
	int width() default 0;

	/**
	 * 检查非空
	 **/
	boolean checkNotNull() default false;

	/**
	 * 唯一性
	 */
	boolean uniqueness() default false;

	/**
	 * 可根据其他属性生成的Code(如果没有设置,则根据其他属性生成)
	 */
	String rule() default "";
}
/**
 * <p>
 *     字典
 * </p>
 *
 * @author lisonglin
 * @version 1.0
 * @since 2022/4/25 19:42
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Dict {
	/**
	 * 对应字典名称
	 **/
	Class dictClass();
}
/**
 * <p>
 *     字段映射
 * </p>
 *
 * @author lisonglin
 * @version 1.0
 * @since 2022/5/6 19:27
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelMapping {
	/**
	 * 映射名
	 */
	String mappingName() default "";

	/**
	 * 是否必填
	 */
	boolean isRequired() default false;

}
/**
 * <p>
 *     默认值注解
 * </p>
 *
 * @author lisonglin
 * @version 1.0
 * @since 2022/4/25 18:28
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelToValue {
	String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TableName {
    String value() default "";
}

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    String value() default "";
}

@TableName("ZJDSP_ZJDZD")
public class ZjdspZjdzd implements Serializable {

	/**
	 *
	 */
	@JSONField(name = "iid")
	@Column("IID")
	private String iid;

	/**
	 *
	 */
	@ExcelToValue(value = Constants.INPUT_INDEX)
	@JSONField(name = "input_index")
	@Column("INPUT_INDEX")
	private String inputIndex;

	/**
	 * 要素代码
	 */
	@ExcelToValue(value = YsdmConstans.YSDM_ZRZ)
	@JSONField(name = "ysdm")
	@Column("YSDM")
	private String ysdm;

	/**
	 * 所有权人代码
	 */
	@ExcelImport(exportName = "所有权人代码", checkNotNull = true, uniqueness = true)
	@JSONField(name = "suyqrdm")
	@Column("SUYQRDM")
	private String suyqrdm;

	/**
	 * 宅基地代码(所有权人代码+6位顺序号)
	 */
	@ExcelMapping(mappingName = "ZjdspZjdzd")
	@ExcelImport(exportName = "宅基地代码", checkNotNull = true, uniqueness = true, rule = "SUYQRDM")
	@JSONField(name = "zjddm")
	@Column("ZJDDM")
	private String zjddm;

	@ExcelToValue
	private ZjdspZjdzdxx zjdspZjdzdxx;
}
@TableName("ZJDSP_ZJDZDXX")
public class ZjdspZjdzdxx implements Serializable {

	/**
	 *
	 */
	@JSONField(name = "iid")
	@Column("IID")
	private String iid;

	/**
	 *
	 */
	@ExcelToValue(value = Constants.INPUT_INDEX)
	@JSONField(name = "input_index")
	@Column("INPUT_INDEX")
	private String inputIndex;

	/**
	 * 要素代码
	 */
	@ExcelToValue(value = YsdmConstans.YSDM_ZJDZDXX)
	@JSONField(name = "ysdm")
	@Column("YSDM")
	private String ysdm;

	/**
	 * 宅基地代码
	 */
	@ExcelMapping(mappingName = "ZjdspZjdzdxx", isRequired = true)
	@JSONField(name = "zjddm")
	@Column("ZJDDM")
	private String zjddm;
}
package antu.zjdsjjg.tool;

import antu.com.Database;
import antu.com.annotations.Column;
import antu.com.annotations.TableName;
import antu.com.tools.LogUtils;
import antu.zjdsjjg.action.AppConfig;
import antu.zjdsjjg.domian.*;
import antu.zjdsjjg.domian.constants.Constants;
import antu.zjdsjjg.domian.constants.ExcelConstants;
import antu.zjdsjjg.model.ZjdspDrlsLog;
import antu.zjdsjjg.model.port.ObjectLocal;
import antu.zjdsjjg.model.vo.ContentVo;
import antu.zjdsjjg.provider.wh.GetWhAdapterProvider;
import antu.zjdsjjg.provider.wh.GetWhAdapterProviderImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StopWatch;
import org.springframework.web.multipart.MultipartFile;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>
 * excel工具类
 * </p>
 *
 * @author lisonglin
 * @version 1.0
 * @since 2022/3/9 14:10
 */
public class ExcelTool {

	/**
	 * 导入excel
	 *
	 * @param multipartFile 文件
	 * @param sheet         工作表
	 * @param jsonData      json
	 * @return 导入的数据
	 */
	public static List<Object> importExcel(MultipartFile multipartFile, String jsonData, String token,
	                                       String... sheet) {
		JSONObject jsonObject = JSONObject.parseObject(jsonData);
		String xzqdm = jsonObject.getString("xzqdm");
		String userid = jsonObject.getString("userid");
		String cjzzdm = jsonObject.getString("cjzzdm");
		String zjzzdm = jsonObject.getString("zjzzdm");
		StopWatch watch = new StopWatch();
		watch.start();
		// 获取用户名
		String userSql = "select name from st_employee where employee_id = ?";
		Database atpDb = new Database(AppConfig.atp_db);
		String userName = atpDb.firstOrDefault(String.class, userSql, userid);
		// 日志记录初始值
		ZjdspDrlsLog zjdspDrlsLog = new ZjdspDrlsLog();
		// 设置行政区代码
		zjdspDrlsLog.setXzqdm(xzqdm);
		// 设置操作系统名
		zjdspDrlsLog.setMis(AppConfig.zjdsjjg_db);
		// 设置导入时间为系统时间
		zjdspDrlsLog.setImportDate(new java.sql.Timestamp(System.currentTimeMillis()));
		// 设置导入人
		zjdspDrlsLog.setCreateUser(userName);
		zjdspDrlsLog.setCjzzdm(cjzzdm);
		zjdspDrlsLog.setZjzzdm(zjzzdm);
		if (!checkFile(multipartFile)) {
			LogUtils.warn("上传的文件类型出错!");
			// 设置导入状态为失败
			zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
			// 设置导入类型
			zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
			// 设置失败原因
			zjdspDrlsLog.setContent("上传的文件类型出错!");
			return null;
		}
		Workbook workBook = getWorkBook(multipartFile);
		return parseExcel(workBook, zjdspDrlsLog, xzqdm, token, watch, sheet);
	}

	/**
	 * 日期类型转换
	 *
	 * @param cell 单元格
	 * @return 字符串的时间
	 */
	public static String stringDateProcess(Cell cell) {
		String result;
		// 处理日期格式、时间格式
		short dataFormat = cell.getCellStyle().getDataFormat();
		if (HSSFDateUtil.isCellDateFormatted(cell)) {
			SimpleDateFormat sdf = null;
			if (cell.getCellStyle().getDataFormat() == HSSFDataFormat.getBuiltinFormat("h:mm")) {
				sdf = new SimpleDateFormat("HH:mm");
			} else {
				// 日期
				sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			}
			Date date = cell.getDateCellValue();
			result = sdf.format(date);
		} else if (dataFormat == ExcelConstants.EXCEL_DATE_MD) {
			// 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58)
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			double value = cell.getNumericCellValue();
			Date date = DateUtil.getJavaDate(value);
			result = sdf.format(date);
		} else {
			double value = cell.getNumericCellValue();
			// int类型
			if (String.valueOf(value).indexOf(".0") > 0) {
				result = String.valueOf(value).substring(0, String.valueOf(value).indexOf(".0"));
			} else {
				// double类型
				DecimalFormat format = new DecimalFormat();
				int len = (value + "").length() - (value + "").indexOf(".") - 1;
				if (len > 0) {
					format.applyPattern("#." + StringUtils.repeat("0", len));
				} else {
					format.applyPattern("#");
				}
				result = format.format(value);
			}
		}
		return result;
	}

	/**
	 * 转换excel数据类型
	 *
	 * @param cell 单元格
	 * @return 转换后的数据
	 */
	private static String convertCellValueToString(Cell cell) {
		if (cell == null || cell.toString().trim().equals(Constants.EMPTY)) {
			return null;
		}
		String cellValue;
		switch (cell.getCellTypeEnum()) {
			case BOOLEAN:
				// 读取布尔
				cellValue = String.valueOf(cell.getBooleanCellValue());
				break;
			case NUMERIC:
				// 读取日期格式
				cellValue = stringDateProcess(cell);
				break;
			case FORMULA:
				// 读取公式
				cellValue = String.valueOf(cell.getCellFormula());
				break;
			case STRING:
				// 读取String
				cellValue = String.valueOf(cell.getStringCellValue());
				break;
			case ERROR:
				// 读取故障
				cellValue = "非法字符";
				break;
			case _NONE:
			case BLANK:
				cellValue = "";
				break;
			default:
				cellValue = "未知类型";
		}
		return cellValue;
	}

	/**
	 * 获取字段键值对 如:{所有权人代码=["setSuyqrdm", "yyyy-MM-dd", “”, “91360421MA35XB656Y”]}
	 * 键值对关系:
	 * Excel表头的字段名称 = [字段set方法名, 时间格式, 需要匹配的字典, 默认值]
	 *
	 * @param clazz 实体类
	 * @return 字段键值对
	 */
	public static List<Object> getFieldMap(Class<?> clazz, Object catObj) {
		Field[] fields = clazz.getDeclaredFields();
		List<Object> objectList = new ArrayList<>();
		// 将所有标有Annotation的字段,也就是允许导入数据的字段,放入到一个map中
		Map<String, ObjectLocal> fieldMap = new HashMap<>(10);
		Map<String, Object[]> childrenSetMap = new HashMap<>(2);
		try {
			// 循环读取所有字段
			for (Field field : fields) {
				putMap(clazz, fieldMap, field, catObj, childrenSetMap);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		objectList.add(fieldMap);
		objectList.add(childrenSetMap);
		return objectList;
	}

	/**
	 * 将对象实例添加进Map中(处理一些默认值)
	 *
	 * @param clazz       bean
	 * @param fieldMap    字段Map
	 * @param field       字段属性
	 * @param newInstance 实例化对象
	 * @throws Exception 异常
	 */
	private static void putMap(Class<?> clazz, Map<String, ObjectLocal> fieldMap, Field field, Object newInstance,
	                           Map<String, Object[]> childrenSetMap) throws Exception {
		ExcelImport exa = field.getAnnotation(ExcelImport.class);
		ExcelToValue eValue = field.getAnnotation(ExcelToValue.class);
		boolean isExist = field.isAnnotationPresent(ExcelToValue.class);
		Dict dict = field.getAnnotation(Dict.class);
		Column column = field.getAnnotation(Column.class);
		ExcelMapping excelMapping = field.getAnnotation(ExcelMapping.class);
		Enum[] enumConstants = null;
		if (null != excelMapping){
			String fieldName = field.getName();
			PropertyDescriptor propertyDescriptor = new PropertyDescriptor(fieldName, clazz);
			Method writeMethod = propertyDescriptor.getWriteMethod();
			Method readMethod = propertyDescriptor.getReadMethod();
			boolean required = excelMapping.isRequired();
			String mappingName = excelMapping.mappingName();
			if (required) {
				// zjdxx
				childrenSetMap.put(mappingName, new Object[]{writeMethod});
			} else {
				// zjdzd
				childrenSetMap.put(mappingName, new Object[]{readMethod});
			}
		}
		if (exa != null) {
			String fieldName = field.getName();
			// 数据库映射值
			String value = column.value();
			PropertyDescriptor propertyDescriptor = new PropertyDescriptor(fieldName, clazz);
			Method writeMethod = propertyDescriptor.getWriteMethod();
			Method readMethod = propertyDescriptor.getReadMethod();
			// 非空检查
			boolean checkNotNull = exa.checkNotNull();
			// 唯一性检查
			boolean uniqueness = exa.uniqueness();
			// 可生成性检查
			String generativeCode = exa.rule();
			Method dictMethod = null;
			if (dict != null) {
				Class<Enum> aClass = dict.dictClass();
				// 获取所有枚举实例
				enumConstants = aClass.getEnumConstants();
				dictMethod = aClass.getDeclaredMethod("getNameByRole", String.class);
			}
			ObjectLocal objectLocal = new ObjectLocal();
			objectLocal.setWriteMethod(writeMethod);
			objectLocal.setReadMethod(readMethod);
			objectLocal.setCheckNotNull(checkNotNull);
			objectLocal.setUniqueness(uniqueness);
			objectLocal.setGenerativeCode(generativeCode);
			objectLocal.setDictMethod(dictMethod);
			objectLocal.setEnumConstants(enumConstants);
			objectLocal.setValue(value);
			objectLocal.setNewInstance(newInstance);
			fieldMap.put(exa.exportName(), objectLocal);
		}
		if (isExist) {
			// 默认值
			String value = eValue.value();
			String fieldName2 = field.getName();
			PropertyDescriptor propertyDescriptor2 = new PropertyDescriptor(fieldName2, clazz);
			Method writeMethod2 = propertyDescriptor2.getWriteMethod();
			if (!StringUtil.isNullOrEmpty(value)) {
				writeMethod2.invoke(newInstance, value);
			} else {
				// 内部对象
				Class<?> type = field.getType();
				// 内部对象
				Object instance = type.newInstance();
				for (Field declaredField : type.getDeclaredFields()) {
					putMap(type, fieldMap, declaredField, instance, childrenSetMap);
				}
				// 内部对象set进包裹对象
				childrenSetMap.put("children", new Object[]{writeMethod2, newInstance, instance});
			}
		}
	}

	/**
	 * 解析excel
	 *
	 * @param workbook     工作簿
	 * @param zjdspDrlsLog 日志
	 * @param sheets       工作表
	 * @return 解析的数据
	 */
	public static List<Object> parseExcel(Workbook workbook, ZjdspDrlsLog zjdspDrlsLog, String xzqdm, String token,
	                                      StopWatch watch, String... sheets) {
		List<Object> resultList = new CopyOnWriteArrayList<>();
		List<Object> resultList2 = new CopyOnWriteArrayList<>();
		// 返回的真实行数
		AtomicInteger realRow = new AtomicInteger(0);
		// 连续空行数
		AtomicInteger emptyRow = new AtomicInteger(0);
		// 失败行数
		AtomicInteger failureRow = new AtomicInteger(0);
		// 主体内容,包括成功条数和失败条数,失败原因,以及失败的数据
		List<ContentVo> contentVoList = new ArrayList<>();
		Database zjdsjjgDb = new Database(AppConfig.zjdsjjg_db);
		// 解析sheet

		for (String sheet : sheets) {
			try {
				String clazzName = ExcelEnum.getNameByRole(sheet);
				Class<?> classType = Class.forName(clazzName);
				Object catObj = classType.newInstance();
				TableName tableName = catObj.getClass().getAnnotation(TableName.class);
				String table = tableName.value();
				// 获取自定义注解的信息
				List<Object> list = getFieldMap(classType, catObj);
				Map<String, ObjectLocal> fieldMap = (Map<String, ObjectLocal>) list.get(0);
				SimpleObject simpleObject = new SimpleObject();
				Map<String, String> ruleMap = new HashMap<>(10);
				Sheet wbSheet = workbook.getSheet(sheet);
				// 校验sheet是否合法
				if (sheet == null) {
					// 设置导入状态为失败
					zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
					// 设置导入类型
					zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
					// 设置失败原因
					zjdspDrlsLog.setContent("解析Excel失败,该sheet不存在!");
					continue;
				}
				// 获取第一行数据
				int firstRowNum = wbSheet.getFirstRowNum();
				Row firstRow = wbSheet.getRow(firstRowNum);
				if (null == firstRow) {
					LogUtils.warn("解析Excel失败,在第一行没有读取到任何数据!");
					// 设置导入状态为失败
					zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
					// 设置导入类型
					zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
					// 设置失败原因
					zjdspDrlsLog.setContent("解析Excel失败,在第一行没有读取到任何数据!");
					continue;
				}
				// 第二行数据
				int startRow = firstRowNum + 2;
				// 获取物理行数,不包括空行或隔行
				int lastRowNum = wbSheet.getLastRowNum();
				if (lastRowNum > 1000) {
					lastRowNum = 1000;
				}

				// 取得标题头行
				Row titleRow = wbSheet.getRow(1);
				// 得到第一行的所有列
				Iterator<Cell> cellTitle = titleRow.cellIterator();
				// 将标题的文字内容放入到一个map中。
				Map<Integer, String> titleMap = new HashMap<>(16);
				// 循环标题所有的列
				for (int i = 0; cellTitle.hasNext(); i++) {
					Cell cell = cellTitle.next();
					if (cell == null) {
						continue;
					}
					String value = cell.getStringCellValue().trim();
					titleMap.put(i, value);
				}
				int titleSize = titleMap.size();
				// 计算有内容行
				for (int i = startRow; i <= lastRowNum; i++) {
					Row row = wbSheet.getRow(i);
					// 连续空行数超过一百行,跳过后续内容
					int emptyRowToInt = emptyRow.get();
					if (emptyRowToInt >= 100) {
						break;
					}
					if (null == row) {
						emptyRow.getAndIncrement();
						continue;
					}
					// 连续空行列数
					AtomicInteger emptyColumn = new AtomicInteger(0);
					// 遍历一行的列
					for (int rowNum = 0; rowNum < titleSize; rowNum++) {
						Cell cell = row.getCell(rowNum);
						if (cell == null || CellType.BLANK == cell.getCellTypeEnum()) {
							emptyColumn.incrementAndGet();
							int i2 = emptyColumn.get();
							// 连续空行列数大于标题数,空行数加一
							if (i2 >= titleSize) {
								emptyRow.getAndIncrement();
							}
						}
					}
					if (emptyRow.get() <= 0) {
						realRow.getAndIncrement();
					}
				}

				// 解析内容行
				for (int i = 0; i < realRow.get(); i++) {
					int traverse = startRow + i;
					Row row = wbSheet.getRow(traverse);
					boolean checkAndConvert = true;
					// 解析内容列
					for (int rowNum = 0; rowNum < titleSize; rowNum++) {
						Cell cell = row.getCell(rowNum);
						// 对应的标题
						String title = titleMap.get(rowNum);
						// 拥有的字段mao和Excel的标题匹配
						if (fieldMap.containsKey(title)) {
							ObjectLocal obj = fieldMap.get(title);
							ObjectLocal objectLocal = obj;
							// 不为空检查
							boolean checkNotNull = obj.isCheckNotNull();
							if (cell == null || CellType.BLANK == cell.getCellTypeEnum()) {
								// 检验数据是否合法
								if (checkNotNull) {
									// 日志记录(失败的导入)
									checkContent(failureRow, contentVoList, title, "", ExcelConstants.CHECK_NOT_NULL);
									realRow.incrementAndGet();
									continue;
								}
							}

							// 父类实例化对象
							checkAndConvert = checkAndConvert(cell, objectLocal, zjdsjjgDb, failureRow, contentVoList, title
									, ruleMap, xzqdm, token, table);
						}
					}
					if (checkAndConvert){
						// 子类set进父类
						Map<String, Object[]> childrenSetMap = (Map<String, Object[]>) list.get(1);
						Object[] children = childrenSetMap.get("children");
						//Method writeMethod = (Method) children[0];
						Object newInstance = children[1];
						Object instance = children[2];
						Object[] newInstanceObjects = childrenSetMap.get(newInstance.getClass().getSimpleName());
						Object[] instanceObjects = childrenSetMap.get(instance.getClass().getSimpleName());
						Method instanceReadMethod = (Method) newInstanceObjects[0];
						Method newInstanceWriteMethod = (Method) instanceObjects[0];
						// zjdxx.setZjddm(zjdzd.getZjddm)
						newInstanceWriteMethod.invoke(instance, instanceReadMethod.invoke(newInstance));
						//writeMethod.invoke(newInstance, instance);
						simpleObject.setInstance(instance);
						simpleObject.setNewInstance(newInstance);
						SimpleObject simpleObject2 = SerializationUtils.clone(simpleObject);
						if (simpleObject2.getInstance() != null) {
							resultList2.add(simpleObject2.getInstance());
						}
						resultList.add(simpleObject2.getNewInstance());
					}
				}
				try {
					zjdsjjgDb.BeginTransaction();
					String newInstanceSql = SqlTool.genSqlInsert(simpleObject.getNewInstance(),
							resultList.size());
					if (resultList2.size() > 0) {
						String instanceSql = SqlTool.genSqlInsert(simpleObject.getInstance(), resultList2.size());
						Object[] resultColumn = SqlTool.genColumn(resultList2);
						zjdsjjgDb.executeUpdate(instanceSql, resultColumn);
					}
					zjdsjjgDb.executeUpdate(newInstanceSql, SqlTool.genColumn(resultList));
					zjdsjjgDb.CompleteTransaction();
				} catch (Exception e) {
					zjdsjjgDb.AbortTransaction();
					e.printStackTrace();
				}



				// 日志记录 (导入总数)
				LogUtils.info("》》》》" + sheet + ",共有:" + realRow + "行");
				zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_INFO);
				zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_INFO);
				String dataCount = String.format("》》》》 %s工作簿,总共有{ %s }条数据", sheet, realRow);
				zjdspDrlsLog.setDataCount(dataCount);
				int failureRowToInt = failureRow.get();
				int realRowToInt = realRow.get();
				int result = realRowToInt - failureRowToInt;
				String contentStr = JSON.toJSON(contentVoList).toString();
				String content = String.format("入库成功{ %s }条,导入失败{ %s }条。导入失败原因:%s", result, failureRowToInt,
						contentStr);
				zjdspDrlsLog.setContent(content);

			} catch (Exception e) {
				// 设置导入状态为失败
				zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
				// 设置导入类型
				zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
				// 设置失败原因
				zjdspDrlsLog.setContent(e.getMessage());
				throw new RuntimeException(e);
			} finally {
				try {
					watch.stop();
					zjdspDrlsLog.setTotalTime("总共耗时:" + watch.getTotalTimeSeconds() + "秒");
					zjdsjjgDb.BeginTransaction();
					zjdsjjgDb.insert(zjdspDrlsLog);
					zjdsjjgDb.CompleteTransaction();
				} catch (Exception e) {
					zjdsjjgDb.AbortTransaction();
					e.printStackTrace();
				}

				try {
					workbook.close();
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
		}

		return resultList;
	}

	private static void checkContent(AtomicInteger failureRow, List<ContentVo> contentVoList, String titleString,
	                                 String cellValue, String reason) {
		ContentVo contentVo = new ContentVo();
		failureRow.getAndIncrement();
		// 失败的数据关键字
		contentVo.setKey(titleString + ":" + cellValue);
		// 失败原因
		contentVo.setReason(reason);
		contentVoList.add(contentVo);
	}

	private static boolean checkAndConvert(Cell cell, ObjectLocal obj, Database zjdsjjgDb, AtomicInteger failureRow,
	                                      List<ContentVo> contentVoList, String titleString,
	                                      Map<String, String> ruleMap, String xzqdm, String token, String table) throws IllegalAccessException, InvocationTargetException {
		// set方法
		Method setMethod = obj.getWriteMethod();
		// 字典
		Method dictMethod = obj.getDictMethod();
		// 父类实例化对象
		Object clazz = obj.getNewInstance();
		// 枚举类实例化对象
		Enum[] anEnum = obj.getEnumConstants();
		// 唯一性检查
		boolean uniqueness = obj.isUniqueness();
		// 可生成性检查
		String rule = obj.getGenerativeCode();
		// 数据库映射值
		String column = obj.getValue();
		// 检验数据是否合法
		if (uniqueness) {
			// 检查唯一性
			String cellValue = cell.getStringCellValue();
			String sqlFormat = "select %s from %s where %s = ?";
			String sql = String.format(sqlFormat, column.toLowerCase(), table, column.toLowerCase());
			// 查询数据库
			String index = zjdsjjgDb.firstOrDefault(String.class, sql, cellValue);
			if (!StringUtil.isNullOrEmpty(index)) {
				// 唯一性检查失败
				// 日志记录
				LogUtils.info("》》》》" + cellValue + "已存在,请检查");
				// 失败内容
				checkContent(failureRow, contentVoList, titleString, cellValue, ExcelConstants.CHECK_NOT_UNIQUENESS);
				return false;
			} else {
				// 唯一性检查成功
				// 将有唯一性的数据放入Map集合
				// map存放 <数据库映射值, 单元格值>
				ruleMap.put(column, cellValue);
				if (StringUtil.isNullOrEmpty(cellValue)) {
					if (!StringUtil.isNullOrEmpty(rule)) {
						// 检查可生成性
						if (ruleMap.containsKey(rule)) {
							// 根据集合中的唯一性数据生成对应的数据
							// 那么生成规则是什么呢?
							String genRule = ruleMap.get(rule);
							GetWhAdapterProvider whAdapterProvider = new GetWhAdapterProviderImpl();
							String wh = whAdapterProvider.getWh(column, xzqdm, genRule, token);
							setMethod.invoke(clazz, wh);
						}
					} else {
						// 不检查可生成性
						convert(cell, setMethod, dictMethod, clazz, anEnum);
					}
				} else {
					// 不检查可生成性
					convert(cell, setMethod, dictMethod, clazz, anEnum);
				}
			}
		} else {
			convert(cell, setMethod, dictMethod, clazz, anEnum);
		}
		return true;
	}

	private static void convert(Cell cell, Method setMethod, Method dictMethod, Object clazz, Enum[] anEnum) throws IllegalAccessException, InvocationTargetException {
		// 得到setter方法的参数
		Type[] ts = setMethod.getGenericParameterTypes();
		String xclass = ts[0].toString();
		// 判断参数类型
		if ("class java.lang.String".equals(xclass)) {
			// 拥有字典的场合
			if (null != dictMethod) {
				// 字典映射后的value enum实例化对象
				Object dictValue = dictMethod.invoke(anEnum, convertCellValueToString(cell));
				setMethod.invoke(clazz, dictValue);
			} else {
				setMethod.invoke(clazz, convertCellValueToString(cell));
			}
		} else if ("class java.util.Date".equals(xclass)) {
			setMethod.invoke(clazz, !StringUtil.isNullOrEmpty(convertCellValueToString(cell)) ?
					DataTimeTool.StrToDate(convertCellValueToString(cell)) : null);
		} else if ("class java.lang.Boolean".equals(xclass)) {
			setMethod.invoke(clazz, convertCellValueToString(cell));
		} else if ("class java.lang.Integer".equals(xclass)) {
			setMethod.invoke(clazz, !StringUtils.isBlank(convertCellValueToString(cell)) ?
					Integer.parseInt(convertCellValueToString(cell)) : 0);
		} else if ("class java.lang.Long".equals(xclass)) {
			setMethod.invoke(clazz, convertCellValueToString(cell));
		} else if ("class java.lang.Double".equals(xclass)) {
			setMethod.invoke(clazz, !StringUtils.isBlank(convertCellValueToString(cell)) ?
					Double.parseDouble(convertCellValueToString(cell)) : 0D);
		} else if ("class java.lang.Object".equals(xclass)) {
			setMethod.invoke(clazz, convertCellValueToString(cell));
		}
	}

	/**
	 * 根据文件后缀名类型获取对应的工作簿对象
	 *
	 * @param multipartFile 导入的文件
	 * @return 包含文件数据的工作簿对象
	 */
	public static Workbook getWorkBook(MultipartFile multipartFile) {
		//获得文件名
		String fileName = multipartFile.getOriginalFilename();
		Workbook workbook = null;
		try {
			InputStream ips = multipartFile.getInputStream();
			if (Objects.requireNonNull(fileName).endsWith(ExcelConstants.EXCEL_XLSX_TYPE)) {
				//2007 及2007以上
				workbook = new XSSFWorkbook(ips);
			} else if (fileName.endsWith(ExcelConstants.EXCEL_XLS_TYPE)) {
				//2003
				workbook = new HSSFWorkbook(ips);
			} else {
				throw new NoSuchMethodError("没有能够处理此文件的方法");
			}
		} catch (IOException e) {
			e.getMessage();
		}
		return workbook;
	}

	/**
	 * 检查文件类型
	 *
	 * @param file 导入的文件
	 * @return 是否是XLS或XLSX文件
	 */
	public static boolean checkFile(MultipartFile file) {
		//判断文件是否存在
		if (null == file) {
			return false;
		}
		//获得文件名
		String fileName = file.getOriginalFilename();
		//判断文件是否是excel文件
		return Objects.requireNonNull(fileName).endsWith(ExcelConstants.EXCEL_XLSX_TYPE) || fileName.endsWith(ExcelConstants.EXCEL_XLS_TYPE);
	}
}

class SimpleObject implements Serializable {
	private Object newInstance;

	private Object instance;

	public Object getInstance() {
		return instance;
	}

	public void setInstance(Object instance) {
		this.instance = instance;
	}

	public Object getNewInstance() {
		return newInstance;
	}

	public void setNewInstance(Object newInstance) {
		this.newInstance = newInstance;
	}
}

public class ObjectLocal implements Serializable {

	private Method writeMethod;
	private Method readMethod;
	private boolean checkNotNull;
	private boolean uniqueness;
	private String generativeCode;
	private Method dictMethod;
	private Enum[] enumConstants;
	private String value;
	private Object newInstance;

	public Method getWriteMethod() {
		return writeMethod;
	}

	public void setWriteMethod(Method writeMethod) {
		this.writeMethod = writeMethod;
	}

	public Method getReadMethod() {
		return readMethod;
	}

	public void setReadMethod(Method readMethod) {
		this.readMethod = readMethod;
	}

	public boolean isCheckNotNull() {
		return checkNotNull;
	}

	public void setCheckNotNull(boolean checkNotNull) {
		this.checkNotNull = checkNotNull;
	}

	public boolean isUniqueness() {
		return uniqueness;
	}

	public void setUniqueness(boolean uniqueness) {
		this.uniqueness = uniqueness;
	}

	public String getGenerativeCode() {
		return generativeCode;
	}

	public void setGenerativeCode(String generativeCode) {
		this.generativeCode = generativeCode;
	}

	public Method getDictMethod() {
		return dictMethod;
	}

	public void setDictMethod(Method dictMethod) {
		this.dictMethod = dictMethod;
	}

	public Enum[] getEnumConstants() {
		return enumConstants;
	}

	public void setEnumConstants(Enum[] enumConstants) {
		this.enumConstants = enumConstants;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public Object getNewInstance() {
		return newInstance;
	}

	public void setNewInstance(Object newInstance) {
		this.newInstance = newInstance;
	}
}

参照连接:https://blog.csdn.net/fangchao2011/article/details/105534252
https://blog.csdn.net/lbq15735104044/article/details/108980532


版权声明:本文为qq_41520636原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。