• 通过easyexcel实现数据导入功能


    上一篇文章已经实现了简单的数据导出功能,这篇文章也介绍一下怎么通过easyexcel从excel表格中导入数据。

    首先,需要在实体类中添加需要导出的字段,@ExcelIgnore注解表示该字段不会被导出到excel,当然,导入的时候也不会读这个字段。

    1. package com.example.springboot.entity;
    2. import com.alibaba.excel.annotation.ExcelIgnore;
    3. import com.alibaba.excel.annotation.ExcelProperty;
    4. import com.baomidou.mybatisplus.annotation.IdType;
    5. import com.baomidou.mybatisplus.annotation.TableField;
    6. import com.baomidou.mybatisplus.annotation.TableId;
    7. import com.baomidou.mybatisplus.annotation.TableName;
    8. import com.fasterxml.jackson.annotation.JsonFormat;
    9. import lombok.Data;
    10. import java.io.Serializable;
    11. import java.time.LocalDateTime;
    12. /**
    13. * 歌曲
    14. * @author heyunlin
    15. * @version 1.0
    16. */
    17. @Data
    18. @TableName("song")
    19. public class Song implements Serializable {
    20. private static final long serialVersionUID = 18L;
    21. @ExcelProperty("歌曲编号")
    22. @TableId(type = IdType.INPUT)
    23. private String id;
    24. /**
    25. * 歌曲名
    26. */
    27. @ExcelProperty("歌曲名")
    28. private String name;
    29. /**
    30. * 歌手
    31. */
    32. @ExcelProperty("歌手")
    33. private String singer;
    34. /**
    35. * 描述信息
    36. */
    37. @ExcelProperty("描述信息")
    38. private String note;
    39. /**
    40. * 最后一次修改时间
    41. */
    42. @ExcelIgnore
    43. @TableField("last_update_time")
    44. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    45. private LocalDateTime lastUpdateTime;
    46. }

    一、前端代码

    在之前的easyui-crud项目的基础上修改,切换到最新代码分支springboot-crud2.0

    springboot+mybatis实现增删查改的入门项目。icon-default.png?t=N7T8https://gitee.com/he-yunlin/springboot-crud.git在原来的页面上添加一个对话框,对话框内放一个easyui的filebox,同时,让filebox镶嵌在一个form表单内,因为要对该表单进行必填验证,只有选择了文件才能点击上传按钮。

    index.html

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>easyui crud应用title>
    6. <link rel="stylesheet" href="/css/themes/icon.css" />
    7. <link rel="stylesheet" href="/css/themes/default/easyui.css" />
    8. <script src="/js/jquery.min.js">script>
    9. <script src="/js/jquery.easyui.min.js">script>
    10. <script src="/js/easyui-lang-zh_CN.js">script>
    11. <script src="/js/datagrid-filter.js">script>
    12. <script src="/js/index.js">script>
    13. head>
    14. <body>
    15. <div id="import_dialog" style="display:none;">
    16. <form id="import_form">
    17. <table style="border-spacing:5px;">
    18. <tr>
    19. <td>上传文件:td>
    20. <td><input id="select_file" />td>
    21. tr>
    22. <tr>
    23. <td>文件名称:td>
    24. <td><div id="file-name">div>td>
    25. tr>
    26. <tr>
    27. <td>文件大小:td>
    28. <td><div id="file-size">div>td>
    29. tr>
    30. table>
    31. form>
    32. div>
    33. <table id="song_list">table>
    34. body>
    35. html>

    index.js

    在原来的js代码中添加以下代码,这里渲染了刚刚在页面中添加的对话框和输入框,然后在表格的头部工具栏中添加了一个导入按钮。

    1. let form = new FormData();
    2. function importHandler() {
    3. requestUrl = "/song/import";
    4. $("#file-name").empty();
    5. $("#file-size").empty();
    6. $("#import_dialog").dialog("open");
    7. }
    8. $(document).ready(function() {
    9. $("#select_file").filebox({
    10. buttonText: "选择文件",
    11. width: 200,
    12. required: true,
    13. onChange: function() {
    14. let file = $(this).context.ownerDocument.activeElement.files[0];
    15. form.append("file", file);
    16. $("#file-name").html(file.name);
    17. $("#file-size").html((file.size / 1024).toFixed(1) + "KB");
    18. }
    19. })
    20. $("#import_dialog").dialog({
    21. title: "数据导入",
    22. modal: true,
    23. closed: true,
    24. closable: true,
    25. draggable: false,
    26. buttons: [{
    27. iconCls: "icon-ok",
    28. text: "导入",
    29. handler: function() {
    30. let bool = $("#import_form").form("validate");
    31. if (bool) {
    32. $.ajax({
    33. url: requestUrl,
    34. data: form,
    35. cache: false,
    36. async: true,
    37. type: "POST",
    38. dataType: "json",
    39. processData: false,
    40. contentType: false,
    41. success: function (response) {
    42. $.messager.show({
    43. title: "系统消息",
    44. timeout: 5000,
    45. showType: "slide",
    46. msg: response.message,
    47. });
    48. $("#import_dialog").dialog("close");
    49. $("#member_list").datagrid("reload");
    50. },
    51. error: function (resp) {
    52. // 请求有响应
    53. if (resp && resp.responseJSON) {
    54. let response = resp.responseJSON;
    55. let status = resp.status;
    56. if (status) {
    57. let message;
    58. if (status === 404) { // 404 not found
    59. if (response.path) {
    60. message = "路径" + response.path + "不存在。";
    61. } else {
    62. message = response.message;
    63. }
    64. } else {
    65. message = response.message;
    66. }
    67. $.messager.alert("系统提示", message, "error");
    68. console.log("响应状态码:" + status + ", 响应消息:" + message);
    69. } else {
    70. console.log("请求没有响应状态码~");
    71. }
    72. } else {
    73. console.log("请求无响应~");
    74. }
    75. }
    76. });
    77. } else {
    78. $.messager.alert("系统提示", "请选择文件", "warning");
    79. }
    80. }
    81. }, {
    82. iconCls: "icon-cancel",
    83. text: "取消",
    84. handler: function() {
    85. $("#select_file").filebox("initValue", null);
    86. $("#import_dialog").dialog("close");
    87. form.delete("file");
    88. }
    89. }]
    90. });
    91. let datagrid = $("#song_list").datagrid({
    92. url: "/song/selectByPage",
    93. title: "歌曲列表",
    94. toolbar: [{
    95. iconCls: "icon-upload",
    96. text: "导入",
    97. handler: function() {
    98. importHandler();
    99. }
    100. }],
    101. columns: [[
    102. {field: "id", title: "id", width: 200},
    103. {field: "name", title: "name", width: 200, editor: "textbox"},
    104. {field: "singer", title: "singer", width: 200, editor: "textbox"},
    105. {field: "note", title: "note", width: 200, editor: "textbox"},
    106. {field: "lastUpdateTime", title: "lastUpdateTime", width: 200, sortable: true}
    107. ]]
    108. });
    109. });

    二、后端代码

    controller

    在controller中添加一个接口,请求类型为post,路径为/import,因为import是java关键字,所以方法名不能使用import,改成importData。

    1. /**
    2. * @author heyunlin
    3. * @version 1.0
    4. */
    5. @RestController
    6. @RequestMapping(path = "/song", produces="application/json;charset=utf-8")
    7. public class SongController {
    8. private final SongService songService;
    9. @Autowired
    10. public SongController(SongService songService) {
    11. this.songService = songService;
    12. }
    13. @RequestMapping(value = "/import", method = RequestMethod.POST)
    14. public void importData(MultipartFile file) throws IOException {
    15. songService.importData(file);
    16. }
    17. }

    service

    SongService接口添加importData()方法

    1. /**
    2. * @author heyunlin
    3. * @version 1.0
    4. */
    5. public interface SongService {
    6. void importData(MultipartFile file) throws IOException;
    7. }

    SongServiceImpl

    通过easyexcel的API读取上传的文件,然后根据读取的结果,判断插入或修改现有数据。

    1. /**
    2. * @author heyunlin
    3. * @version 1.0
    4. */
    5. @Service
    6. public class SongServiceImpl implements SongService {
    7. private final SongMapper songMapper;
    8. @Autowired
    9. public SongServiceImpl(SongMapper songMapper) {
    10. this.songMapper = songMapper;
    11. }
    12. @Override
    13. public void importData(MultipartFile file) throws IOException {
    14. EasyExcel.read(file.getInputStream(), Song.class, new ReadListener() {
    15. @Override
    16. public void invoke(Song data, AnalysisContext context) {
    17. Song song = songMapper.selectById(data.getId());
    18. if (song == null) {
    19. songMapper.insert(data);
    20. } else {
    21. songMapper.updateById(data);
    22. }
    23. }
    24. @Override
    25. public void doAfterAllAnalysed(AnalysisContext context) {
    26. }
    27. }).sheet().doRead();
    28. }
    29. }

    三、功能预览

    如图,选择文件之后会显示文件的预览信息,点击导入,就会通过ajax上传文件到后台controller接口。

    点击导入按钮,后端读取到了表格数据,并在控制台打印。

    四、后端代码改进

    上面的代码有一个很明显的问题

    1. // 频繁查询数据库,excel表有多少行就查询多少次
    2. Song song = songMapper.selectById(data.getId());

    频繁访问数据库问题

    对此,需要进行相应的改进,减少查询次数。

    最有效的方法是一次性查询所有歌曲,然后以ID为key保存到一个map里,当然,这只适合数据量不是特别大的情况。

    优化后的代码如下:

    1. @Override
    2. public void importData(MultipartFile file) throws IOException {
    3. // 查询全部歌曲信息
    4. List list = songMapper.selectList(null);
    5. // 把歌曲信息以ID为key保存到map中
    6. Map map = new HashMap<>(list.size());
    7. for (Song song : list) {
    8. map.put(song.getId(), song);
    9. }
    10. // 读excel表
    11. EasyExcel.read(file.getInputStream(), Song.class, new ReadListener() {
    12. @Override
    13. public void invoke(Song data, AnalysisContext context) {
    14. if (map.containsKey(data.getId())) {
    15. songMapper.updateById(data);
    16. } else {
    17. songMapper.insert(data);
    18. }
    19. }
    20. @Override
    21. public void doAfterAllAnalysed(AnalysisContext context) {
    22. }
    23. }).sheet().doRead();
    24. }

  • 相关阅读:
    数据资产管理解决方案:构建高效、安全的数据生态体系
    Java 面试需要掌握哪些内容?
    SpringCloud Hystrix熔断之线程池
    ET框架6.0分析三、网络通信
    14.Java RMI学习以及制作远程服务
    134.加油站
    cmake 命令
    基于ABP和Magicodes实现Excel导出操作
    React中组件通信01——props
    刘未鹏 - 我在南大的七年 有感
  • 原文地址:https://blog.csdn.net/heyl163_/article/details/134508154