需求分析

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

思路

数据映射

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

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

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

字典

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

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

这样子,会影响性能。

下个一版本,改为enum枚举类。
JAVA 之POI导入批量新增、批量检查、日志记录、失败原因、失败条数、数据库映射 - 图1

有效行数

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

什么是有效行数?

每一行有真正的数据,而不是空行数据。
JAVA 之POI导入批量新增、批量检查、日志记录、失败原因、失败条数、数据库映射 - 图2

这个逻辑就是有三个原子类。
JAVA 之POI导入批量新增、批量检查、日志记录、失败原因、失败条数、数据库映射 - 图3
我认为当连续空行数大于100行,那么没必要继续遍历查询。

计算有内容行核心逻辑

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

难点与问题

第一个难点

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的值。

实现

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

  1. /**
  2. * <p>
  3. * excel导入注解
  4. * </p>
  5. *
  6. * @author lisonglin
  7. * @version 1.0
  8. * @since 2022/4/25 16:19
  9. */
  10. @Documented
  11. @Inherited
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. public @interface ExcelImport {
  15. /**
  16. * 与excel标题头对应
  17. */
  18. String exportName();
  19. /**
  20. * 转换格式, 如时间类型 yyyy-MM-dd HH:mm:ss
  21. */
  22. String pattern() default "";
  23. /**
  24. * 在excel中位置
  25. */
  26. int order() default 0;
  27. /**
  28. * 长度
  29. **/
  30. int width() default 0;
  31. /**
  32. * 检查非空
  33. **/
  34. boolean checkNotNull() default false;
  35. /**
  36. * 唯一性
  37. */
  38. boolean uniqueness() default false;
  39. /**
  40. * 可根据其他属性生成的Code(如果没有设置,则根据其他属性生成)
  41. */
  42. String rule() default "";
  43. }
  1. /**
  2. * <p>
  3. * 字典
  4. * </p>
  5. *
  6. * @author lisonglin
  7. * @version 1.0
  8. * @since 2022/4/25 19:42
  9. */
  10. @Documented
  11. @Inherited
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. public @interface Dict {
  15. /**
  16. * 对应字典名称
  17. **/
  18. Class dictClass();
  19. }
  1. /**
  2. * <p>
  3. * 字段映射
  4. * </p>
  5. *
  6. * @author lisonglin
  7. * @version 1.0
  8. * @since 2022/5/6 19:27
  9. */
  10. @Documented
  11. @Inherited
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. public @interface ExcelMapping {
  15. /**
  16. * 映射名
  17. */
  18. String mappingName() default "";
  19. /**
  20. * 是否必填
  21. */
  22. boolean isRequired() default false;
  23. }
  1. /**
  2. * <p>
  3. * 默认值注解
  4. * </p>
  5. *
  6. * @author lisonglin
  7. * @version 1.0
  8. * @since 2022/4/25 18:28
  9. */
  10. @Documented
  11. @Inherited
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. public @interface ExcelToValue {
  15. String value() default "";
  16. }
  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface TableName {
  5. String value() default "";
  6. }
  1. @Target({ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Column {
  5. String value() default "";
  6. }
  1. @TableName("ZJDSP_ZJDZD")
  2. public class ZjdspZjdzd implements Serializable {
  3. /**
  4. *
  5. */
  6. @JSONField(name = "iid")
  7. @Column("IID")
  8. private String iid;
  9. /**
  10. *
  11. */
  12. @ExcelToValue(value = Constants.INPUT_INDEX)
  13. @JSONField(name = "input_index")
  14. @Column("INPUT_INDEX")
  15. private String inputIndex;
  16. /**
  17. * 要素代码
  18. */
  19. @ExcelToValue(value = YsdmConstans.YSDM_ZRZ)
  20. @JSONField(name = "ysdm")
  21. @Column("YSDM")
  22. private String ysdm;
  23. /**
  24. * 所有权人代码
  25. */
  26. @ExcelImport(exportName = "所有权人代码", checkNotNull = true, uniqueness = true)
  27. @JSONField(name = "suyqrdm")
  28. @Column("SUYQRDM")
  29. private String suyqrdm;
  30. /**
  31. * 宅基地代码(所有权人代码+6位顺序号)
  32. */
  33. @ExcelMapping(mappingName = "ZjdspZjdzd")
  34. @ExcelImport(exportName = "宅基地代码", checkNotNull = true, uniqueness = true, rule = "SUYQRDM")
  35. @JSONField(name = "zjddm")
  36. @Column("ZJDDM")
  37. private String zjddm;
  38. @ExcelToValue
  39. private ZjdspZjdzdxx zjdspZjdzdxx;
  40. }
  1. @TableName("ZJDSP_ZJDZDXX")
  2. public class ZjdspZjdzdxx implements Serializable {
  3. /**
  4. *
  5. */
  6. @JSONField(name = "iid")
  7. @Column("IID")
  8. private String iid;
  9. /**
  10. *
  11. */
  12. @ExcelToValue(value = Constants.INPUT_INDEX)
  13. @JSONField(name = "input_index")
  14. @Column("INPUT_INDEX")
  15. private String inputIndex;
  16. /**
  17. * 要素代码
  18. */
  19. @ExcelToValue(value = YsdmConstans.YSDM_ZJDZDXX)
  20. @JSONField(name = "ysdm")
  21. @Column("YSDM")
  22. private String ysdm;
  23. /**
  24. * 宅基地代码
  25. */
  26. @ExcelMapping(mappingName = "ZjdspZjdzdxx", isRequired = true)
  27. @JSONField(name = "zjddm")
  28. @Column("ZJDDM")
  29. private String zjddm;
  30. }
  1. package antu.zjdsjjg.tool;
  2. import antu.com.Database;
  3. import antu.com.annotations.Column;
  4. import antu.com.annotations.TableName;
  5. import antu.com.tools.LogUtils;
  6. import antu.zjdsjjg.action.AppConfig;
  7. import antu.zjdsjjg.domian.*;
  8. import antu.zjdsjjg.domian.constants.Constants;
  9. import antu.zjdsjjg.domian.constants.ExcelConstants;
  10. import antu.zjdsjjg.model.ZjdspDrlsLog;
  11. import antu.zjdsjjg.model.port.ObjectLocal;
  12. import antu.zjdsjjg.model.vo.ContentVo;
  13. import antu.zjdsjjg.provider.wh.GetWhAdapterProvider;
  14. import antu.zjdsjjg.provider.wh.GetWhAdapterProviderImpl;
  15. import com.alibaba.fastjson.JSON;
  16. import com.alibaba.fastjson.JSONObject;
  17. import org.apache.commons.lang.StringUtils;
  18. import org.apache.commons.lang3.SerializationUtils;
  19. import org.apache.poi.hssf.usermodel.HSSFDataFormat;
  20. import org.apache.poi.hssf.usermodel.HSSFDateUtil;
  21. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  22. import org.apache.poi.ss.usermodel.*;
  23. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  24. import org.springframework.util.StopWatch;
  25. import org.springframework.web.multipart.MultipartFile;
  26. import java.beans.PropertyDescriptor;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.Serializable;
  30. import java.lang.reflect.*;
  31. import java.text.DecimalFormat;
  32. import java.text.SimpleDateFormat;
  33. import java.util.*;
  34. import java.util.concurrent.CopyOnWriteArrayList;
  35. import java.util.concurrent.atomic.AtomicInteger;
  36. /**
  37. * <p>
  38. * excel工具类
  39. * </p>
  40. *
  41. * @author lisonglin
  42. * @version 1.0
  43. * @since 2022/3/9 14:10
  44. */
  45. public class ExcelTool {
  46. /**
  47. * 导入excel
  48. *
  49. * @param multipartFile 文件
  50. * @param sheet 工作表
  51. * @param jsonData json
  52. * @return 导入的数据
  53. */
  54. public static List<Object> importExcel(MultipartFile multipartFile, String jsonData, String token,
  55. String... sheet) {
  56. JSONObject jsonObject = JSONObject.parseObject(jsonData);
  57. String xzqdm = jsonObject.getString("xzqdm");
  58. String userid = jsonObject.getString("userid");
  59. String cjzzdm = jsonObject.getString("cjzzdm");
  60. String zjzzdm = jsonObject.getString("zjzzdm");
  61. StopWatch watch = new StopWatch();
  62. watch.start();
  63. // 获取用户名
  64. String userSql = "select name from st_employee where employee_id = ?";
  65. Database atpDb = new Database(AppConfig.atp_db);
  66. String userName = atpDb.firstOrDefault(String.class, userSql, userid);
  67. // 日志记录初始值
  68. ZjdspDrlsLog zjdspDrlsLog = new ZjdspDrlsLog();
  69. // 设置行政区代码
  70. zjdspDrlsLog.setXzqdm(xzqdm);
  71. // 设置操作系统名
  72. zjdspDrlsLog.setMis(AppConfig.zjdsjjg_db);
  73. // 设置导入时间为系统时间
  74. zjdspDrlsLog.setImportDate(new java.sql.Timestamp(System.currentTimeMillis()));
  75. // 设置导入人
  76. zjdspDrlsLog.setCreateUser(userName);
  77. zjdspDrlsLog.setCjzzdm(cjzzdm);
  78. zjdspDrlsLog.setZjzzdm(zjzzdm);
  79. if (!checkFile(multipartFile)) {
  80. LogUtils.warn("上传的文件类型出错!");
  81. // 设置导入状态为失败
  82. zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
  83. // 设置导入类型
  84. zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
  85. // 设置失败原因
  86. zjdspDrlsLog.setContent("上传的文件类型出错!");
  87. return null;
  88. }
  89. Workbook workBook = getWorkBook(multipartFile);
  90. return parseExcel(workBook, zjdspDrlsLog, xzqdm, token, watch, sheet);
  91. }
  92. /**
  93. * 日期类型转换
  94. *
  95. * @param cell 单元格
  96. * @return 字符串的时间
  97. */
  98. public static String stringDateProcess(Cell cell) {
  99. String result;
  100. // 处理日期格式、时间格式
  101. short dataFormat = cell.getCellStyle().getDataFormat();
  102. if (HSSFDateUtil.isCellDateFormatted(cell)) {
  103. SimpleDateFormat sdf = null;
  104. if (cell.getCellStyle().getDataFormat() == HSSFDataFormat.getBuiltinFormat("h:mm")) {
  105. sdf = new SimpleDateFormat("HH:mm");
  106. } else {
  107. // 日期
  108. sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  109. }
  110. Date date = cell.getDateCellValue();
  111. result = sdf.format(date);
  112. } else if (dataFormat == ExcelConstants.EXCEL_DATE_MD) {
  113. // 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58)
  114. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  115. double value = cell.getNumericCellValue();
  116. Date date = DateUtil.getJavaDate(value);
  117. result = sdf.format(date);
  118. } else {
  119. double value = cell.getNumericCellValue();
  120. // int类型
  121. if (String.valueOf(value).indexOf(".0") > 0) {
  122. result = String.valueOf(value).substring(0, String.valueOf(value).indexOf(".0"));
  123. } else {
  124. // double类型
  125. DecimalFormat format = new DecimalFormat();
  126. int len = (value + "").length() - (value + "").indexOf(".") - 1;
  127. if (len > 0) {
  128. format.applyPattern("#." + StringUtils.repeat("0", len));
  129. } else {
  130. format.applyPattern("#");
  131. }
  132. result = format.format(value);
  133. }
  134. }
  135. return result;
  136. }
  137. /**
  138. * 转换excel数据类型
  139. *
  140. * @param cell 单元格
  141. * @return 转换后的数据
  142. */
  143. private static String convertCellValueToString(Cell cell) {
  144. if (cell == null || cell.toString().trim().equals(Constants.EMPTY)) {
  145. return null;
  146. }
  147. String cellValue;
  148. switch (cell.getCellTypeEnum()) {
  149. case BOOLEAN:
  150. // 读取布尔
  151. cellValue = String.valueOf(cell.getBooleanCellValue());
  152. break;
  153. case NUMERIC:
  154. // 读取日期格式
  155. cellValue = stringDateProcess(cell);
  156. break;
  157. case FORMULA:
  158. // 读取公式
  159. cellValue = String.valueOf(cell.getCellFormula());
  160. break;
  161. case STRING:
  162. // 读取String
  163. cellValue = String.valueOf(cell.getStringCellValue());
  164. break;
  165. case ERROR:
  166. // 读取故障
  167. cellValue = "非法字符";
  168. break;
  169. case _NONE:
  170. case BLANK:
  171. cellValue = "";
  172. break;
  173. default:
  174. cellValue = "未知类型";
  175. }
  176. return cellValue;
  177. }
  178. /**
  179. * 获取字段键值对 如:{所有权人代码=["setSuyqrdm", "yyyy-MM-dd", “”, “91360421MA35XB656Y”]}
  180. * 键值对关系:
  181. * Excel表头的字段名称 = [字段set方法名, 时间格式, 需要匹配的字典, 默认值]
  182. *
  183. * @param clazz 实体类
  184. * @return 字段键值对
  185. */
  186. public static List<Object> getFieldMap(Class<?> clazz, Object catObj) {
  187. Field[] fields = clazz.getDeclaredFields();
  188. List<Object> objectList = new ArrayList<>();
  189. // 将所有标有Annotation的字段,也就是允许导入数据的字段,放入到一个map中
  190. Map<String, ObjectLocal> fieldMap = new HashMap<>(10);
  191. Map<String, Object[]> childrenSetMap = new HashMap<>(2);
  192. try {
  193. // 循环读取所有字段
  194. for (Field field : fields) {
  195. putMap(clazz, fieldMap, field, catObj, childrenSetMap);
  196. }
  197. } catch (Exception e) {
  198. throw new RuntimeException(e);
  199. }
  200. objectList.add(fieldMap);
  201. objectList.add(childrenSetMap);
  202. return objectList;
  203. }
  204. /**
  205. * 将对象实例添加进Map中(处理一些默认值)
  206. *
  207. * @param clazz bean
  208. * @param fieldMap 字段Map
  209. * @param field 字段属性
  210. * @param newInstance 实例化对象
  211. * @throws Exception 异常
  212. */
  213. private static void putMap(Class<?> clazz, Map<String, ObjectLocal> fieldMap, Field field, Object newInstance,
  214. Map<String, Object[]> childrenSetMap) throws Exception {
  215. ExcelImport exa = field.getAnnotation(ExcelImport.class);
  216. ExcelToValue eValue = field.getAnnotation(ExcelToValue.class);
  217. boolean isExist = field.isAnnotationPresent(ExcelToValue.class);
  218. Dict dict = field.getAnnotation(Dict.class);
  219. Column column = field.getAnnotation(Column.class);
  220. ExcelMapping excelMapping = field.getAnnotation(ExcelMapping.class);
  221. Enum[] enumConstants = null;
  222. if (null != excelMapping){
  223. String fieldName = field.getName();
  224. PropertyDescriptor propertyDescriptor = new PropertyDescriptor(fieldName, clazz);
  225. Method writeMethod = propertyDescriptor.getWriteMethod();
  226. Method readMethod = propertyDescriptor.getReadMethod();
  227. boolean required = excelMapping.isRequired();
  228. String mappingName = excelMapping.mappingName();
  229. if (required) {
  230. // zjdxx
  231. childrenSetMap.put(mappingName, new Object[]{writeMethod});
  232. } else {
  233. // zjdzd
  234. childrenSetMap.put(mappingName, new Object[]{readMethod});
  235. }
  236. }
  237. if (exa != null) {
  238. String fieldName = field.getName();
  239. // 数据库映射值
  240. String value = column.value();
  241. PropertyDescriptor propertyDescriptor = new PropertyDescriptor(fieldName, clazz);
  242. Method writeMethod = propertyDescriptor.getWriteMethod();
  243. Method readMethod = propertyDescriptor.getReadMethod();
  244. // 非空检查
  245. boolean checkNotNull = exa.checkNotNull();
  246. // 唯一性检查
  247. boolean uniqueness = exa.uniqueness();
  248. // 可生成性检查
  249. String generativeCode = exa.rule();
  250. Method dictMethod = null;
  251. if (dict != null) {
  252. Class<Enum> aClass = dict.dictClass();
  253. // 获取所有枚举实例
  254. enumConstants = aClass.getEnumConstants();
  255. dictMethod = aClass.getDeclaredMethod("getNameByRole", String.class);
  256. }
  257. ObjectLocal objectLocal = new ObjectLocal();
  258. objectLocal.setWriteMethod(writeMethod);
  259. objectLocal.setReadMethod(readMethod);
  260. objectLocal.setCheckNotNull(checkNotNull);
  261. objectLocal.setUniqueness(uniqueness);
  262. objectLocal.setGenerativeCode(generativeCode);
  263. objectLocal.setDictMethod(dictMethod);
  264. objectLocal.setEnumConstants(enumConstants);
  265. objectLocal.setValue(value);
  266. objectLocal.setNewInstance(newInstance);
  267. fieldMap.put(exa.exportName(), objectLocal);
  268. }
  269. if (isExist) {
  270. // 默认值
  271. String value = eValue.value();
  272. String fieldName2 = field.getName();
  273. PropertyDescriptor propertyDescriptor2 = new PropertyDescriptor(fieldName2, clazz);
  274. Method writeMethod2 = propertyDescriptor2.getWriteMethod();
  275. if (!StringUtil.isNullOrEmpty(value)) {
  276. writeMethod2.invoke(newInstance, value);
  277. } else {
  278. // 内部对象
  279. Class<?> type = field.getType();
  280. // 内部对象
  281. Object instance = type.newInstance();
  282. for (Field declaredField : type.getDeclaredFields()) {
  283. putMap(type, fieldMap, declaredField, instance, childrenSetMap);
  284. }
  285. // 内部对象set进包裹对象
  286. childrenSetMap.put("children", new Object[]{writeMethod2, newInstance, instance});
  287. }
  288. }
  289. }
  290. /**
  291. * 解析excel
  292. *
  293. * @param workbook 工作簿
  294. * @param zjdspDrlsLog 日志
  295. * @param sheets 工作表
  296. * @return 解析的数据
  297. */
  298. public static List<Object> parseExcel(Workbook workbook, ZjdspDrlsLog zjdspDrlsLog, String xzqdm, String token,
  299. StopWatch watch, String... sheets) {
  300. List<Object> resultList = new CopyOnWriteArrayList<>();
  301. List<Object> resultList2 = new CopyOnWriteArrayList<>();
  302. // 返回的真实行数
  303. AtomicInteger realRow = new AtomicInteger(0);
  304. // 连续空行数
  305. AtomicInteger emptyRow = new AtomicInteger(0);
  306. // 失败行数
  307. AtomicInteger failureRow = new AtomicInteger(0);
  308. // 主体内容,包括成功条数和失败条数,失败原因,以及失败的数据
  309. List<ContentVo> contentVoList = new ArrayList<>();
  310. Database zjdsjjgDb = new Database(AppConfig.zjdsjjg_db);
  311. // 解析sheet
  312. for (String sheet : sheets) {
  313. try {
  314. String clazzName = ExcelEnum.getNameByRole(sheet);
  315. Class<?> classType = Class.forName(clazzName);
  316. Object catObj = classType.newInstance();
  317. TableName tableName = catObj.getClass().getAnnotation(TableName.class);
  318. String table = tableName.value();
  319. // 获取自定义注解的信息
  320. List<Object> list = getFieldMap(classType, catObj);
  321. Map<String, ObjectLocal> fieldMap = (Map<String, ObjectLocal>) list.get(0);
  322. SimpleObject simpleObject = new SimpleObject();
  323. Map<String, String> ruleMap = new HashMap<>(10);
  324. Sheet wbSheet = workbook.getSheet(sheet);
  325. // 校验sheet是否合法
  326. if (sheet == null) {
  327. // 设置导入状态为失败
  328. zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
  329. // 设置导入类型
  330. zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
  331. // 设置失败原因
  332. zjdspDrlsLog.setContent("解析Excel失败,该sheet不存在!");
  333. continue;
  334. }
  335. // 获取第一行数据
  336. int firstRowNum = wbSheet.getFirstRowNum();
  337. Row firstRow = wbSheet.getRow(firstRowNum);
  338. if (null == firstRow) {
  339. LogUtils.warn("解析Excel失败,在第一行没有读取到任何数据!");
  340. // 设置导入状态为失败
  341. zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
  342. // 设置导入类型
  343. zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
  344. // 设置失败原因
  345. zjdspDrlsLog.setContent("解析Excel失败,在第一行没有读取到任何数据!");
  346. continue;
  347. }
  348. // 第二行数据
  349. int startRow = firstRowNum + 2;
  350. // 获取物理行数,不包括空行或隔行
  351. int lastRowNum = wbSheet.getLastRowNum();
  352. if (lastRowNum > 1000) {
  353. lastRowNum = 1000;
  354. }
  355. // 取得标题头行
  356. Row titleRow = wbSheet.getRow(1);
  357. // 得到第一行的所有列
  358. Iterator<Cell> cellTitle = titleRow.cellIterator();
  359. // 将标题的文字内容放入到一个map中。
  360. Map<Integer, String> titleMap = new HashMap<>(16);
  361. // 循环标题所有的列
  362. for (int i = 0; cellTitle.hasNext(); i++) {
  363. Cell cell = cellTitle.next();
  364. if (cell == null) {
  365. continue;
  366. }
  367. String value = cell.getStringCellValue().trim();
  368. titleMap.put(i, value);
  369. }
  370. int titleSize = titleMap.size();
  371. // 计算有内容行
  372. for (int i = startRow; i <= lastRowNum; i++) {
  373. Row row = wbSheet.getRow(i);
  374. // 连续空行数超过一百行,跳过后续内容
  375. int emptyRowToInt = emptyRow.get();
  376. if (emptyRowToInt >= 100) {
  377. break;
  378. }
  379. if (null == row) {
  380. emptyRow.getAndIncrement();
  381. continue;
  382. }
  383. // 连续空行列数
  384. AtomicInteger emptyColumn = new AtomicInteger(0);
  385. // 遍历一行的列
  386. for (int rowNum = 0; rowNum < titleSize; rowNum++) {
  387. Cell cell = row.getCell(rowNum);
  388. if (cell == null || CellType.BLANK == cell.getCellTypeEnum()) {
  389. emptyColumn.incrementAndGet();
  390. int i2 = emptyColumn.get();
  391. // 连续空行列数大于标题数,空行数加一
  392. if (i2 >= titleSize) {
  393. emptyRow.getAndIncrement();
  394. }
  395. }
  396. }
  397. if (emptyRow.get() <= 0) {
  398. realRow.getAndIncrement();
  399. }
  400. }
  401. // 解析内容行
  402. for (int i = 0; i < realRow.get(); i++) {
  403. int traverse = startRow + i;
  404. Row row = wbSheet.getRow(traverse);
  405. boolean checkAndConvert = true;
  406. // 解析内容列
  407. for (int rowNum = 0; rowNum < titleSize; rowNum++) {
  408. Cell cell = row.getCell(rowNum);
  409. // 对应的标题
  410. String title = titleMap.get(rowNum);
  411. // 拥有的字段mao和Excel的标题匹配
  412. if (fieldMap.containsKey(title)) {
  413. ObjectLocal obj = fieldMap.get(title);
  414. ObjectLocal objectLocal = obj;
  415. // 不为空检查
  416. boolean checkNotNull = obj.isCheckNotNull();
  417. if (cell == null || CellType.BLANK == cell.getCellTypeEnum()) {
  418. // 检验数据是否合法
  419. if (checkNotNull) {
  420. // 日志记录(失败的导入)
  421. checkContent(failureRow, contentVoList, title, "", ExcelConstants.CHECK_NOT_NULL);
  422. realRow.incrementAndGet();
  423. continue;
  424. }
  425. }
  426. // 父类实例化对象
  427. checkAndConvert = checkAndConvert(cell, objectLocal, zjdsjjgDb, failureRow, contentVoList, title
  428. , ruleMap, xzqdm, token, table);
  429. }
  430. }
  431. if (checkAndConvert){
  432. // 子类set进父类
  433. Map<String, Object[]> childrenSetMap = (Map<String, Object[]>) list.get(1);
  434. Object[] children = childrenSetMap.get("children");
  435. //Method writeMethod = (Method) children[0];
  436. Object newInstance = children[1];
  437. Object instance = children[2];
  438. Object[] newInstanceObjects = childrenSetMap.get(newInstance.getClass().getSimpleName());
  439. Object[] instanceObjects = childrenSetMap.get(instance.getClass().getSimpleName());
  440. Method instanceReadMethod = (Method) newInstanceObjects[0];
  441. Method newInstanceWriteMethod = (Method) instanceObjects[0];
  442. // zjdxx.setZjddm(zjdzd.getZjddm)
  443. newInstanceWriteMethod.invoke(instance, instanceReadMethod.invoke(newInstance));
  444. //writeMethod.invoke(newInstance, instance);
  445. simpleObject.setInstance(instance);
  446. simpleObject.setNewInstance(newInstance);
  447. SimpleObject simpleObject2 = SerializationUtils.clone(simpleObject);
  448. if (simpleObject2.getInstance() != null) {
  449. resultList2.add(simpleObject2.getInstance());
  450. }
  451. resultList.add(simpleObject2.getNewInstance());
  452. }
  453. }
  454. try {
  455. zjdsjjgDb.BeginTransaction();
  456. String newInstanceSql = SqlTool.genSqlInsert(simpleObject.getNewInstance(),
  457. resultList.size());
  458. if (resultList2.size() > 0) {
  459. String instanceSql = SqlTool.genSqlInsert(simpleObject.getInstance(), resultList2.size());
  460. Object[] resultColumn = SqlTool.genColumn(resultList2);
  461. zjdsjjgDb.executeUpdate(instanceSql, resultColumn);
  462. }
  463. zjdsjjgDb.executeUpdate(newInstanceSql, SqlTool.genColumn(resultList));
  464. zjdsjjgDb.CompleteTransaction();
  465. } catch (Exception e) {
  466. zjdsjjgDb.AbortTransaction();
  467. e.printStackTrace();
  468. }
  469. // 日志记录 (导入总数)
  470. LogUtils.info("》》》》" + sheet + ",共有:" + realRow + "行");
  471. zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_INFO);
  472. zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_INFO);
  473. String dataCount = String.format("》》》》 %s工作簿,总共有{ %s }条数据", sheet, realRow);
  474. zjdspDrlsLog.setDataCount(dataCount);
  475. int failureRowToInt = failureRow.get();
  476. int realRowToInt = realRow.get();
  477. int result = realRowToInt - failureRowToInt;
  478. String contentStr = JSON.toJSON(contentVoList).toString();
  479. String content = String.format("入库成功{ %s }条,导入失败{ %s }条。导入失败原因:%s", result, failureRowToInt,
  480. contentStr);
  481. zjdspDrlsLog.setContent(content);
  482. } catch (Exception e) {
  483. // 设置导入状态为失败
  484. zjdspDrlsLog.setHandleType(ExcelConstants.EXCEL_TYPE_ERROR);
  485. // 设置导入类型
  486. zjdspDrlsLog.setGrade(ExcelConstants.EXCEL_GRADE_ERROR);
  487. // 设置失败原因
  488. zjdspDrlsLog.setContent(e.getMessage());
  489. throw new RuntimeException(e);
  490. } finally {
  491. try {
  492. watch.stop();
  493. zjdspDrlsLog.setTotalTime("总共耗时:" + watch.getTotalTimeSeconds() + "秒");
  494. zjdsjjgDb.BeginTransaction();
  495. zjdsjjgDb.insert(zjdspDrlsLog);
  496. zjdsjjgDb.CompleteTransaction();
  497. } catch (Exception e) {
  498. zjdsjjgDb.AbortTransaction();
  499. e.printStackTrace();
  500. }
  501. try {
  502. workbook.close();
  503. } catch (IOException e) {
  504. throw new RuntimeException(e);
  505. }
  506. }
  507. }
  508. return resultList;
  509. }
  510. private static void checkContent(AtomicInteger failureRow, List<ContentVo> contentVoList, String titleString,
  511. String cellValue, String reason) {
  512. ContentVo contentVo = new ContentVo();
  513. failureRow.getAndIncrement();
  514. // 失败的数据关键字
  515. contentVo.setKey(titleString + ":" + cellValue);
  516. // 失败原因
  517. contentVo.setReason(reason);
  518. contentVoList.add(contentVo);
  519. }
  520. private static boolean checkAndConvert(Cell cell, ObjectLocal obj, Database zjdsjjgDb, AtomicInteger failureRow,
  521. List<ContentVo> contentVoList, String titleString,
  522. Map<String, String> ruleMap, String xzqdm, String token, String table) throws IllegalAccessException, InvocationTargetException {
  523. // set方法
  524. Method setMethod = obj.getWriteMethod();
  525. // 字典
  526. Method dictMethod = obj.getDictMethod();
  527. // 父类实例化对象
  528. Object clazz = obj.getNewInstance();
  529. // 枚举类实例化对象
  530. Enum[] anEnum = obj.getEnumConstants();
  531. // 唯一性检查
  532. boolean uniqueness = obj.isUniqueness();
  533. // 可生成性检查
  534. String rule = obj.getGenerativeCode();
  535. // 数据库映射值
  536. String column = obj.getValue();
  537. // 检验数据是否合法
  538. if (uniqueness) {
  539. // 检查唯一性
  540. String cellValue = cell.getStringCellValue();
  541. String sqlFormat = "select %s from %s where %s = ?";
  542. String sql = String.format(sqlFormat, column.toLowerCase(), table, column.toLowerCase());
  543. // 查询数据库
  544. String index = zjdsjjgDb.firstOrDefault(String.class, sql, cellValue);
  545. if (!StringUtil.isNullOrEmpty(index)) {
  546. // 唯一性检查失败
  547. // 日志记录
  548. LogUtils.info("》》》》" + cellValue + "已存在,请检查");
  549. // 失败内容
  550. checkContent(failureRow, contentVoList, titleString, cellValue, ExcelConstants.CHECK_NOT_UNIQUENESS);
  551. return false;
  552. } else {
  553. // 唯一性检查成功
  554. // 将有唯一性的数据放入Map集合
  555. // map存放 <数据库映射值, 单元格值>
  556. ruleMap.put(column, cellValue);
  557. if (StringUtil.isNullOrEmpty(cellValue)) {
  558. if (!StringUtil.isNullOrEmpty(rule)) {
  559. // 检查可生成性
  560. if (ruleMap.containsKey(rule)) {
  561. // 根据集合中的唯一性数据生成对应的数据
  562. // 那么生成规则是什么呢?
  563. String genRule = ruleMap.get(rule);
  564. GetWhAdapterProvider whAdapterProvider = new GetWhAdapterProviderImpl();
  565. String wh = whAdapterProvider.getWh(column, xzqdm, genRule, token);
  566. setMethod.invoke(clazz, wh);
  567. }
  568. } else {
  569. // 不检查可生成性
  570. convert(cell, setMethod, dictMethod, clazz, anEnum);
  571. }
  572. } else {
  573. // 不检查可生成性
  574. convert(cell, setMethod, dictMethod, clazz, anEnum);
  575. }
  576. }
  577. } else {
  578. convert(cell, setMethod, dictMethod, clazz, anEnum);
  579. }
  580. return true;
  581. }
  582. private static void convert(Cell cell, Method setMethod, Method dictMethod, Object clazz, Enum[] anEnum) throws IllegalAccessException, InvocationTargetException {
  583. // 得到setter方法的参数
  584. Type[] ts = setMethod.getGenericParameterTypes();
  585. String xclass = ts[0].toString();
  586. // 判断参数类型
  587. if ("class java.lang.String".equals(xclass)) {
  588. // 拥有字典的场合
  589. if (null != dictMethod) {
  590. // 字典映射后的value enum实例化对象
  591. Object dictValue = dictMethod.invoke(anEnum, convertCellValueToString(cell));
  592. setMethod.invoke(clazz, dictValue);
  593. } else {
  594. setMethod.invoke(clazz, convertCellValueToString(cell));
  595. }
  596. } else if ("class java.util.Date".equals(xclass)) {
  597. setMethod.invoke(clazz, !StringUtil.isNullOrEmpty(convertCellValueToString(cell)) ?
  598. DataTimeTool.StrToDate(convertCellValueToString(cell)) : null);
  599. } else if ("class java.lang.Boolean".equals(xclass)) {
  600. setMethod.invoke(clazz, convertCellValueToString(cell));
  601. } else if ("class java.lang.Integer".equals(xclass)) {
  602. setMethod.invoke(clazz, !StringUtils.isBlank(convertCellValueToString(cell)) ?
  603. Integer.parseInt(convertCellValueToString(cell)) : 0);
  604. } else if ("class java.lang.Long".equals(xclass)) {
  605. setMethod.invoke(clazz, convertCellValueToString(cell));
  606. } else if ("class java.lang.Double".equals(xclass)) {
  607. setMethod.invoke(clazz, !StringUtils.isBlank(convertCellValueToString(cell)) ?
  608. Double.parseDouble(convertCellValueToString(cell)) : 0D);
  609. } else if ("class java.lang.Object".equals(xclass)) {
  610. setMethod.invoke(clazz, convertCellValueToString(cell));
  611. }
  612. }
  613. /**
  614. * 根据文件后缀名类型获取对应的工作簿对象
  615. *
  616. * @param multipartFile 导入的文件
  617. * @return 包含文件数据的工作簿对象
  618. */
  619. public static Workbook getWorkBook(MultipartFile multipartFile) {
  620. //获得文件名
  621. String fileName = multipartFile.getOriginalFilename();
  622. Workbook workbook = null;
  623. try {
  624. InputStream ips = multipartFile.getInputStream();
  625. if (Objects.requireNonNull(fileName).endsWith(ExcelConstants.EXCEL_XLSX_TYPE)) {
  626. //2007 及2007以上
  627. workbook = new XSSFWorkbook(ips);
  628. } else if (fileName.endsWith(ExcelConstants.EXCEL_XLS_TYPE)) {
  629. //2003
  630. workbook = new HSSFWorkbook(ips);
  631. } else {
  632. throw new NoSuchMethodError("没有能够处理此文件的方法");
  633. }
  634. } catch (IOException e) {
  635. e.getMessage();
  636. }
  637. return workbook;
  638. }
  639. /**
  640. * 检查文件类型
  641. *
  642. * @param file 导入的文件
  643. * @return 是否是XLS或XLSX文件
  644. */
  645. public static boolean checkFile(MultipartFile file) {
  646. //判断文件是否存在
  647. if (null == file) {
  648. return false;
  649. }
  650. //获得文件名
  651. String fileName = file.getOriginalFilename();
  652. //判断文件是否是excel文件
  653. return Objects.requireNonNull(fileName).endsWith(ExcelConstants.EXCEL_XLSX_TYPE) || fileName.endsWith(ExcelConstants.EXCEL_XLS_TYPE);
  654. }
  655. }
  656. class SimpleObject implements Serializable {
  657. private Object newInstance;
  658. private Object instance;
  659. public Object getInstance() {
  660. return instance;
  661. }
  662. public void setInstance(Object instance) {
  663. this.instance = instance;
  664. }
  665. public Object getNewInstance() {
  666. return newInstance;
  667. }
  668. public void setNewInstance(Object newInstance) {
  669. this.newInstance = newInstance;
  670. }
  671. }
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