当前位置:   article > 正文

【EasyExcel】第一篇:动态导入excel,生成对应数据库表_easyexcel动态数据导入

easyexcel动态数据导入

背景

需求是:根据导入的excel,读取sheet空间,每个sheet对应生成一张数据库的表

一个excel包含一个或多个sheet

前言

本文章适用于动态创建表,动态创建表字段、填充数据。

一、依赖

  1. <!--操作Excel工具依赖-->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>easyexcel</artifactId>
  5. <version>3.0.5</version>
  6. </dependency>
  7. <!--mybatis 分页插件-->
  8. <dependency>
  9. <groupId>com.github.pagehelper</groupId>
  10. <artifactId>pagehelper-spring-boot-starter</artifactId>
  11. <version>1.4.5</version>
  12. </dependency>
  13. <!-- fastjson -->
  14. <dependency>
  15. <groupId>com.alibaba</groupId>
  16. <artifactId>fastjson</artifactId>
  17. <version>1.2.70</version>
  18. </dependency>

 二、数据库记录表

  1. SET NAMES utf8mb4;
  2. SET FOREIGN_KEY_CHECKS = 0;
  3. -- ----------------------------
  4. -- Table structure for sys_data_directory
  5. -- ----------------------------
  6. DROP TABLE IF EXISTS `sys_data_directory`;
  7. CREATE TABLE `sys_data_directory` (
  8. `id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
  9. `data_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据集、源别名',
  10. `parent_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '父节点默认为0',
  11. `distinctid_isn` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '行政区划内码',
  12. `distinctid` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '行政区划',
  13. `distinctid_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '行政区划名称',
  14. `distinctid_all_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '行政区划全名',
  15. `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
  16. `update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
  17. `create_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人id',
  18. `create_user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '创建人名称',
  19. `update_user_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改人id',
  20. `update_user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '修改人名称',
  21. `table_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '导入excel中sheet为表名、表名',
  22. `sort` int(11) NULL DEFAULT 0 COMMENT '排序',
  23. `data_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '数据源描述',
  24. `data_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '01民政、02公安、03对比结果、99其他',
  25. `address_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '01门楼址、02户室址',
  26. PRIMARY KEY (`id`) USING BTREE
  27. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
  28. SET FOREIGN_KEY_CHECKS = 1;

三、导入excel

1.代码

1.1Controller层

  1. @Api(tags = "数据源集合")
  2. @RestController
  3. @RequestMapping("/hvit/dataSet/")
  4. public class SysDataDirectoryController {
  5. @Autowired
  6. private SysDataDirectoryDataService sysDataDirectoryDataService;
  7. @RequestMapping(value = "/importExcel", method = RequestMethod.POST, headers = "content-type=multipart/form-data")
  8. @ApiOperation(value = "导入文件型数据源")
  9. public ResponseEntity importExcel(@RequestParam(value = "file") MultipartFile file, ExcelReq excelReq) throws IOException {
  10. return ResponseEntity.ok(sysDataDirectoryDataService.importExcel(file, excelReq));
  11. }
  12. }

1.2 ExcelReq类

  1. import io.swagger.annotations.ApiModelProperty;
  2. import lombok.Data;
  3. import java.io.Serializable;
  4. @Data
  5. public class ExcelReq implements Serializable {
  6. private static final long serialVersionUID = 1L;
  7. @ApiModelProperty(name = "id", value = "父节点id")
  8. private String id;
  9. @ApiModelProperty(name = "dataType", value = "数据来源类型")
  10. private String dataType;
  11. @ApiModelProperty(name = "anotherName", value = "别名")
  12. private String anotherName;
  13. @ApiModelProperty(name = "addressType", value = "地址类型")
  14. private String addressType;
  15. }

1.3Service类

  1. /***
  2. * 导入文件型数据源
  3. * @param file execl文件
  4. *
  5. * @return
  6. */
  7. @Transactional
  8. public R importExcel(MultipartFile file, ExcelReq excelReq) throws IOException {
  9. if (file == null) {
  10. return R.error("请上传文件!");
  11. }
  12. //获取用户信息
  13. SysUser sysUser = sysUserService.findByUserName(getCurrentLoginUserName());
  14. String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
  15. //文件后缀
  16. String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
  17. if (!Constant.EXCEL_XLSX.equals(fileSuffix) && !Constant.EXCEL_XLS.equals(fileSuffix)) {
  18. return R.error("请导入excel类型文件!");
  19. }
  20. //1.首先获取文件名生成一条记录
  21. String directoryName = StringUtils.isEmpty(excelReq.getAnotherName()) ? fileName : excelReq.getAnotherName();
  22. //创建记录并返回主键id
  23. QueryWrapper<SysDataDirectory> wrapper = new QueryWrapper<>();
  24. wrapper.lambda().eq(SysDataDirectory::getCreateUserId, sysUser.getId());
  25. wrapper.lambda().eq(SysDataDirectory::getDataName, directoryName);
  26. wrapper.lambda().eq(SysDataDirectory::getParentId, excelReq.getId());
  27. int count = sysDataDirectoryService.count(wrapper);
  28. if (count > 0) {
  29. return R.error("数据源集合有重名!");
  30. }
  31. String uuid = createDataSets(directoryName, null, excelReq.getId(), sysUser, excelReq.getDataType(), excelReq.getAddressType());
  32. EasyExcel.read(file.getInputStream(), new ConfigFilterListener(sysDataDirectoryMapper, uuid, sysDataDirectoryService
  33. , excelReq.getDataType(), sysUser, excelReq.getAddressType())).doReadAll();
  34. return R.ok();
  35. }

1.4响应类R

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. /**
  4. *
  5. * @author cz
  6. * @date 2022-11-10
  7. */
  8. public class R extends HashMap<String, Object> {
  9. private static final long serialVersionUID = 1L;
  10. public R() {
  11. put("code", 200);
  12. }
  13. public R(Integer code) {
  14. put("code", code);
  15. put("data", new HashMap<String, Object>());
  16. }
  17. public R(Integer code, String msg) {
  18. put("code", code);
  19. put("msg", msg);
  20. put("data", new HashMap<String, Object>());
  21. }
  22. public static R error() {
  23. return error(500, "未知异常,请联系管理员");
  24. }
  25. public static R errorDebug(String message) {
  26. return error(500, "未知异常 " + message + ",请联系管理员");
  27. }
  28. public static R error(String msg) {
  29. return error(500, msg);
  30. }
  31. public static R error(int code, String msg) {
  32. R r = new R();
  33. r.put("code", code);
  34. r.put("msg", msg);
  35. return r;
  36. }
  37. public R errorInfo(String msg) {
  38. this.put("errorMsg", msg);
  39. return this;
  40. }
  41. public static R ok(String msg) {
  42. R r = new R();
  43. r.put("msg", msg);
  44. r.put("data", new HashMap<String, Object>());
  45. return r;
  46. }
  47. public static R ok(Map<String, Object> map) {
  48. R r = new R();
  49. r.putAll(map);
  50. r.put("data", new HashMap<String, Object>());
  51. return r;
  52. }
  53. public static R ok() {
  54. return new R().put("msg", "success").put("data", new HashMap<String, Object>());
  55. }
  56. public static R ok(Integer size) {
  57. return new R().put("data", new HashMap<String, Object>((int)Math.round(size / 0.75)));
  58. }
  59. @Override
  60. public R put(String key, Object value) {
  61. super.put(key, value);
  62. return this;
  63. }
  64. /**
  65. * 添加返回结果数据
  66. *
  67. * @param key
  68. * @param value
  69. * @return
  70. */
  71. public R putData(String key, Object value) {
  72. Map<String, Object> map = (HashMap<String, Object>)this.get("data");
  73. map.put(key, value);
  74. return this;
  75. }
  76. }

1.5easyExcel监听类ConfigFilterListener

  1. import com.alibaba.excel.context.AnalysisContext;
  2. import com.alibaba.excel.metadata.data.ReadCellData;
  3. import com.alibaba.excel.read.listener.ReadListener;
  4. import com.hvit.data_governance.dataComparison.dao.SysDataDirectoryMapper;
  5. import com.hvit.data_governance.dataComparison.entity.SysDataDirectory;
  6. import com.hvit.data_governance.dataComparison.service.SysDataDirectoryService;
  7. import com.hvit.data_governance.system.entity.SysUser;
  8. import lombok.SneakyThrows;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.scheduling.annotation.Async;
  12. import javax.annotation.Resource;
  13. import java.util.*;
  14. import java.util.stream.Collectors;
  15. @Slf4j
  16. public class ConfigFilterListener implements ReadListener<LinkedHashMap<String, String>> {
  17. /**
  18. * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
  19. */
  20. private static final int BATCH_COUNT = 1000;
  21. private String uuid;
  22. private String dataType;
  23. private String tableName;//表名
  24. private String columnNames;//字段名
  25. private Integer sort = 0;//排序
  26. private String addressType;//地址类型
  27. private List<LinkedHashMap<String, String>> dataSetList = new ArrayList<>();
  28. @Resource
  29. private SysDataDirectoryMapper sysDataDirectoryMapper;
  30. @Autowired
  31. SysDataDirectoryService sysDataDirectoryService;
  32. private SysUser sysUser;
  33. //构造函数
  34. public ConfigFilterListener(SysDataDirectoryMapper sysDataDirectoryMapper, String uuid, SysDataDirectoryService sysDataDirectoryService
  35. , String dataType, SysUser sysUser, String addressType) {
  36. this.uuid = uuid;
  37. this.dataType = dataType;
  38. this.sysUser = sysUser;
  39. this.addressType = addressType;
  40. this.sysDataDirectoryMapper = sysDataDirectoryMapper;
  41. this.sysDataDirectoryService = sysDataDirectoryService;
  42. }
  43. /**
  44. * 这个每一条数据解析都会来调用
  45. */
  46. @SneakyThrows
  47. @Override
  48. public void invoke(LinkedHashMap<String, String> linkedHashMap, AnalysisContext analysisContext) {
  49. //log.info("解析到一条数据:{}", linkedHashMap);
  50. LinkedHashMap<String, String> map = new LinkedHashMap<>();
  51. map.put("uuid", UUID.randomUUID().toString());
  52. Set set = linkedHashMap.keySet();
  53. Iterator iterator = set.iterator();
  54. while (iterator.hasNext()) {
  55. Object next = iterator.next();
  56. map.put(next.toString(), linkedHashMap.get(next));
  57. }
  58. dataSetList.add(map);
  59. // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
  60. if (dataSetList.size() >= BATCH_COUNT) {
  61. //创建插入语句
  62. StringBuffer sb = new StringBuffer("insert into ");
  63. sb.append(this.tableName + " (");
  64. sb.append(this.columnNames + " )");
  65. // 这里也要保存数据,确保最后遗留的数据也存储到数据库
  66. batchInsert(sb.toString(), dataSetList);
  67. // 存储完成清理 list
  68. dataSetList.clear();
  69. }
  70. }
  71. /**
  72. * 所有数据解析完成了 都会来调用
  73. *
  74. * @param analysisContext
  75. */
  76. @SneakyThrows
  77. @Override
  78. public void doAfterAllAnalysed(AnalysisContext analysisContext) {
  79. log.info("进入入库操作--->");
  80. if (dataSetList.size() > 0) {
  81. //创建插入语句
  82. StringBuffer sb = new StringBuffer("insert into ");
  83. sb.append(this.tableName + " (");
  84. sb.append(this.columnNames + " )");
  85. // 这里也要保存数据,确保最后遗留的数据也存储到数据库
  86. batchInsert(sb.toString(), dataSetList);
  87. dataSetList.clear();
  88. log.info("所有数据解析完成!");
  89. }
  90. }
  91. /***
  92. * 读取Excel表格表头
  93. * @param headMap
  94. * @param context
  95. */
  96. @Override
  97. public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
  98. try {
  99. //每次执行前需求清除上次的结果
  100. this.columnNames = null;
  101. // 当前sheet的名称 编码获取类似
  102. String tableName = context.readSheetHolder().getSheetName();
  103. int tableCount = sysDataDirectoryMapper.existsTable(tableName);
  104. List<String> heads = new ArrayList<>();
  105. heads.add("uuid");
  106. if (tableCount > 0) {
  107. tableName += "_"+System.currentTimeMillis();
  108. }
  109. StringBuffer createTableStr = new StringBuffer("CREATE TABLE ");
  110. createTableStr.append(tableName);
  111. createTableStr.append(" (uuid varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,");
  112. Collection<ReadCellData<?>> values = headMap.values();
  113. for (ReadCellData<?> value : values) {
  114. createTableStr.append(value.getStringValue() + " varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,");
  115. heads.add(value.getStringValue());
  116. }
  117. createTableStr.append("PRIMARY KEY (`uuid`) USING BTREE)");
  118. int updateCount = sysDataDirectoryMapper.createTable(createTableStr.toString());
  119. if (updateCount != 0) {
  120. throw new RuntimeException("创建数据库表失败!");
  121. }
  122. //创建成功后,得插入一条对应记录
  123. createDataSets(tableName, tableName, uuid, sysUser, dataType, sort, addressType);
  124. this.tableName = tableName;
  125. this.columnNames = heads.stream().collect(Collectors.joining(","));
  126. sort++;
  127. } catch (Exception ex) {
  128. //sysDataDirectoryService.removeById(uuid);
  129. //throw new RuntimeException("导入失败!请联系管理员!");
  130. }
  131. }
  132. @Override
  133. public boolean hasNext(AnalysisContext context) {
  134. return true;
  135. }
  136. // /**
  137. // * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
  138. // *
  139. // * @param exception
  140. // * @param context
  141. // * @throws Exception
  142. // */
  143. // @Override
  144. // public void onException(Exception exception, AnalysisContext context) {
  145. // log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
  146. // // 如果是某一个单元格的转换异常 能获取到具体行号
  147. // // 如果要获取头的信息 配合invokeHeadMap使用
  148. // if (exception instanceof ExcelDataConvertException) {
  149. // ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
  150. // log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
  151. // excelDataConvertException.getColumnIndex());
  152. // }
  153. // }
  154. @Async
  155. public void batchInsert(String tableString, List<LinkedHashMap<String, String>> list) throws Exception {
  156. sysDataDirectoryMapper.insertTableData(tableString, list);
  157. log.info("存储数据库成功!");
  158. }
  159. /***
  160. * 创建目录结构
  161. * @param directoryName 数据源别名
  162. * @param tableName 表名
  163. * @param parentId 父节点id,默认父节点id为0
  164. * @return
  165. */
  166. public String createDataSets(String directoryName, String tableName, String parentId, SysUser sysUser, String dataType, Integer sort, String addressType) {
  167. SysDataDirectory sysDataDirectory = new SysDataDirectory();
  168. sysDataDirectory.setDataName(directoryName);
  169. sysDataDirectory.setParentId(parentId);
  170. sysDataDirectory.setCreateTime(new Date());
  171. sysDataDirectory.setCreateUserId(sysUser.getId().toString());
  172. sysDataDirectory.setCreateUserName(sysUser.getUserName());
  173. sysDataDirectory.setDistinctid(sysUser.getDistinctid());
  174. sysDataDirectory.setDistinctidAllName(sysUser.getDistinctidAllName());
  175. sysDataDirectory.setDistinctidIsn(sysUser.getDistinctidIsn());
  176. sysDataDirectory.setDistinctidName(sysUser.getDistinctidName());
  177. sysDataDirectory.setUpdateTime(new Date());
  178. sysDataDirectory.setUpdateUserId(sysUser.getId().toString());
  179. sysDataDirectory.setUpdateUserName(sysUser.getUserName());
  180. sysDataDirectory.setSort(sort);
  181. sysDataDirectory.setTableName(tableName);
  182. sysDataDirectory.setDataType(dataType);
  183. sysDataDirectory.setAddressType(addressType);
  184. sysDataDirectoryService.save(sysDataDirectory);
  185. return sysDataDirectory.getId();
  186. }
  187. }

1.6Dao层Mapper

  1. import com.hvit.data_governance.dataComparison.entity.SysDataDirectory;
  2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  3. import org.apache.ibatis.annotations.*;
  4. import java.util.LinkedHashMap;
  5. import java.util.List;
  6. /**
  7. * <p>
  8. * Mapper 接口
  9. * </p>
  10. *
  11. * @author 曹震
  12. * @since 2022-10-24
  13. */
  14. @Mapper
  15. public interface SysDataDirectoryMapper extends BaseMapper<SysDataDirectory> {
  16. @Update("${tableString}")
  17. int createTable(@Param("tableString") String tableString);
  18. /**
  19. * 判断表是否存在
  20. *
  21. * @param tableName 表名称
  22. * @return 结果
  23. * @author yunnuo
  24. */
  25. @Select(" SELECT COUNT(*) as count FROM information_schema.TABLES WHERE table_name = #{tableName}")
  26. Integer existsTable(@Param("tableName") String tableName);
  27. @Insert({"<script>" +
  28. "${tableString} " +
  29. "values" +
  30. "<foreach collection='dataSetList' item='line' index='index' separator=','> " +
  31. "<foreach collection='line.values' item='value' open='(' separator=',' close=')'>" +
  32. "#{value}" +
  33. "</foreach>" +
  34. "</foreach>" +
  35. "</script>"
  36. })
  37. Integer insertTableData(@Param("tableString") String tableString, @Param("dataSetList") List<LinkedHashMap<String, String>> dataSetList);
  38. /***
  39. * 获取表的字段信息
  40. * @param tableName
  41. * @return
  42. */
  43. @Select(" select COLUMN_NAME from information_schema.columns where table_name = #{tableName}")
  44. List<String> getColumnName(@Param("tableName") String tableName);
  45. /***
  46. * 获取表中数据量
  47. * @param tableName
  48. * @return
  49. */
  50. @Select(" select count(1) from ${tableName}")
  51. Integer getTableDataCount(@Param("tableName") String tableName);
  52. }

多个sheet就生成多个表

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/321631
推荐阅读
相关标签
  

闽ICP备14008679号