通过easyexcel实现数据导入功能

news2024/12/25 23:48:17

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

目录

一、前端代码

index.html

index.js

二、后端代码

controller

service

SongServiceImpl

三、功能预览

四、后端代码改进

频繁访问数据库问题


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

package com.example.springboot.entity;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 歌曲
 * @author heyunlin
 * @version 1.0
 */
@Data
@TableName("song")
public class Song implements Serializable {
    private static final long serialVersionUID = 18L;

    @ExcelProperty("歌曲编号")
    @TableId(type = IdType.INPUT)
    private String id;

    /**
     * 歌曲名
     */
    @ExcelProperty("歌曲名")
    private String name;

    /**
     * 歌手
     */
    @ExcelProperty("歌手")
    private String singer;

    /**
     * 描述信息
     */
    @ExcelProperty("描述信息")
    private String note;

    /**
     * 最后一次修改时间
     */
    @ExcelIgnore
    @TableField("last_update_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private LocalDateTime lastUpdateTime;
}

一、前端代码

在之前的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

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>easyui crud应用</title>
		<link rel="stylesheet" href="/css/themes/icon.css" />
		<link rel="stylesheet" href="/css/themes/default/easyui.css" />
		<script src="/js/jquery.min.js"></script>
		<script src="/js/jquery.easyui.min.js"></script>
		<script src="/js/easyui-lang-zh_CN.js"></script>
		<script src="/js/datagrid-filter.js"></script>
		<script src="/js/index.js"></script>
	</head>
	
	<body>
		<div id="import_dialog" style="display:none;">
			<form id="import_form">
				<table style="border-spacing:5px;">
					<tr>
						<td>上传文件:</td>
						<td><input id="select_file" /></td>
					</tr>

					<tr>
						<td>文件名称:</td>
						<td><div id="file-name"></div></td>
					</tr>

					<tr>
						<td>文件大小:</td>
						<td><div id="file-size"></div></td>
					</tr>
				</table>
			</form>
		</div>

		<table id="song_list"></table>
	</body>
</html>

index.js

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

let form = new FormData();

function importHandler() {
	requestUrl = "/song/import";

	$("#file-name").empty();
	$("#file-size").empty();

	$("#import_dialog").dialog("open");
}

$(document).ready(function() { 
    $("#select_file").filebox({
		buttonText: "选择文件",
		width: 200,
		required: true,
		onChange: function() {
			let file = $(this).context.ownerDocument.activeElement.files[0];

			form.append("file", file);

			$("#file-name").html(file.name);
			$("#file-size").html((file.size / 1024).toFixed(1) + "KB");
		}
	})

	$("#import_dialog").dialog({
		title: "数据导入",
		modal: true,
		closed: true,
		closable: true,
		draggable: false,
		buttons: [{
			iconCls: "icon-ok",
			text: "导入",
			handler: function() {
				let bool = $("#import_form").form("validate");

				if (bool) {
					$.ajax({
						url: requestUrl,
						data: form,
						cache: false,
						async: true,
						type: "POST",
						dataType: "json",
						processData: false,
						contentType: false,
						success: function (response) {
							$.messager.show({
								title: "系统消息",
								timeout: 5000,
								showType: "slide",
								msg: response.message,
							});

							$("#import_dialog").dialog("close");
							$("#member_list").datagrid("reload");
						},
						error: function (resp) {
							// 请求有响应
							if (resp && resp.responseJSON) {
								let response = resp.responseJSON;
								let status = resp.status;

								if (status) {
									let message;

									if (status === 404) { // 404 not found
										if (response.path) {
											message = "路径" + response.path + "不存在。";
										} else {
											message = response.message;
										}
									} else {
										message = response.message;
									}

									$.messager.alert("系统提示", message, "error");
									console.log("响应状态码:" + status + ", 响应消息:" + message);
								} else {
									console.log("请求没有响应状态码~");
								}
							} else {
								console.log("请求无响应~");
							}
						}
					});
				} else {
					$.messager.alert("系统提示", "请选择文件", "warning");
				}
			}
		}, {
			iconCls: "icon-cancel",
			text: "取消",
			handler: function() {
				$("#select_file").filebox("initValue", null);
				$("#import_dialog").dialog("close");
				form.delete("file");
			}
		}]
	});

    let datagrid = $("#song_list").datagrid({
		url: "/song/selectByPage",
		title: "歌曲列表",
		toolbar: [{
			iconCls: "icon-upload",
			text: "导入",
			handler: function() {
				importHandler();
			}
		}],
		columns: [[
			{field: "id", title: "id", width: 200},
			{field: "name", title: "name", width: 200, editor: "textbox"},
			{field: "singer", title: "singer", width: 200, editor: "textbox"},
			{field: "note", title: "note", width: 200, editor: "textbox"},
			{field: "lastUpdateTime", title: "lastUpdateTime", width: 200, sortable: true}
		]]
	});

});

二、后端代码

controller

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

/**
 * @author heyunlin
 * @version 1.0
 */
@RestController
@RequestMapping(path = "/song", produces="application/json;charset=utf-8")
public class SongController {

    private final SongService songService;

    @Autowired
    public SongController(SongService songService) {
        this.songService = songService;
    }

    @RequestMapping(value = "/import", method = RequestMethod.POST)
    public void importData(MultipartFile file) throws IOException {
        songService.importData(file);
    }

}

service

SongService接口添加importData()方法

/**
 * @author heyunlin
 * @version 1.0
 */
public interface SongService {

    void importData(MultipartFile file) throws IOException;
}

SongServiceImpl

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Service
public class SongServiceImpl implements SongService {

    private final SongMapper songMapper;

    @Autowired
    public SongServiceImpl(SongMapper songMapper) {
        this.songMapper = songMapper;
    }

    @Override
    public void importData(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), Song.class, new ReadListener<Song>() {
            @Override
            public void invoke(Song data, AnalysisContext context) {
                Song song = songMapper.selectById(data.getId());

                if (song == null) {
                    songMapper.insert(data);
                } else {
                    songMapper.updateById(data);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {

            }
        }).sheet().doRead();
    }

}

三、功能预览

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

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

四、后端代码改进

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

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

频繁访问数据库问题

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

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

优化后的代码如下:

@Override
public void importData(MultipartFile file) throws IOException {
    // 查询全部歌曲信息
    List<Song> list = songMapper.selectList(null);
    
    // 把歌曲信息以ID为key保存到map中
    Map<String, Song> map = new HashMap<>(list.size());

    for (Song song : list) {
        map.put(song.getId(), song);
    }
    
    // 读excel表
    EasyExcel.read(file.getInputStream(), Song.class, new ReadListener<Song>() {
        @Override
        public void invoke(Song data, AnalysisContext context) {
            if (map.containsKey(data.getId())) {
                songMapper.updateById(data);
            } else {
                songMapper.insert(data);
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {

        }
    }).sheet().doRead();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1231462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Run Legends将健身运动游戏化,使用户保持健康并了解Web3游戏

最近&#xff0c;我们有机会采访Talofa Games的首席执行官兼创始人Jenny Xu&#xff0c;一起讨论游戏开发&#xff0c;Talofa Games是Run Legends这款健身游戏的开发工作室。她已经创作了超过一百款游戏&#xff0c;对于推动游戏的可能性并将她的创造力和叙事技巧带入她最喜爱的…

面试题-6

1.精灵图和base64的区别是什么&#xff1f; 精灵图:把多张小图整合到一张大图上,利用定位的一些属性把小图显示在页面上,当访问页面可以减少请求,提高加载速度 base64&#xff1a;传输8bit字节代码的编码方式,把原本二进制形式转为64个字符的单位&#xff0c;最后组成字符串 …

深入解析序列模型:全面阐释 RNN、LSTM 与 Seq2Seq 的秘密

探索序列建模的基础知识和应用。 简介 序列建模是许多领域的一个重要问题&#xff0c;包括自然语言处理 (NLP)、语音识别和语音合成、时间序列预测、音乐生成和「生物信息学」。所有这些任务的共同点是它们需要坚持。接下来的事情的预测是基于历史的。例如&#xff0c;在“哈桑…

漏洞复现--飞企互联FE业务协作平台ShowImageServlet任意文件读取

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

【C/C++】递归算法

信封 某人写了n封信和n个信封&#xff0c;如果所有的信都装错了信封。求所有的信都装错信封共有多少种不同情况 #include <iostream> using namespace std; const int N 30; int n; long f[N];int main() {scanf("%d", &n);f[1] 0, f[2] 1;for (int …

面向开发者的Android

Developerhttps://developer.android.google.cn/?hlzh-cn SDK 平台工具版本说明https://developer.android.google.cn/studio/releases/platform-tools?hlzh-cn#revisions Android SDK Platform-Tools 是 Android SDK 的一个组件。它包含与 Android 平台进行交互的工具…

【Python】给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200

2、问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 样例输入 5 8 3 6 4 9 样例输出 3 4 6 8 9 n int(input()) a list(map(int,input().split())) a.sort() for i in a:print(i,end ) 运行结果&#xff1a;

OceanBase 4.2.1 LTS 发版 | 一体化数据库首个长期支持版本

在刚刚结束的年度发布会上&#xff0c;OceanBase 沿着“一体化”产品战略思路&#xff0c;发布了一体化数据库的首个长期支持版本 4.2.1 LTS。作为 4.0 系列的第一个 LTS 版本&#xff0c;该版本的定位是支撑客户关键业务稳定长久运行&#xff0c;我们非常认真的打磨了这个版本…

OpenVPN Connect使用连接公网VPN服务器实现内网穿透

安装并运行OpenVPN Connect 点击AGREE 添加配置.OVPN文件 点击连接 连接成功 两个内网主机通过公网VPN穿透

pip list 和 conda list的区别

PS : 网上说conda activate了之后就可以随意pip了 可以conda和pip混用 但是安全起见还是尽量用pip 这样就算activate了&#xff0c;进入base虚拟环境了 conda与pip的区别 来源 Conda和pip通常被认为几乎完全相同。虽然这两个工具的某些功能重叠&#xff0c;但它们设计用于不…

I.MX6ULL开发笔记(一)——环境搭建、镜像烧录、网络连接

本系列为使用野火IMX6ULL开发的学习笔记&#xff0c;使用的开发板为如下&#xff1a; 具有的硬件资源有如下&#xff1a; 文章目录 一、环境搭建Win11安装WSL安装串口驱动安装串口工具安装Ubuntu与windows文件互传 二、镜像烧录修改串口终端登录前信息 三、fire-config工具配…

【Python】给定n个十六进制正整数,输出它们对应的八进制数。

3.问题描述 给定n个十六进制正整数&#xff0c;输出它们对应的八进制数。 样例输入 2 39 123ABC 样例输出 71 4435274 n int(input()) li [] # 创建列表 for i in range(n):li.append(input()) # 输入数据 for num in li:if len(num) < 100000: # 判断长度是否符…

HTTP四种请求方式,状态码,请求和响应报文

1.get请求 一般用于获取数据请求参数在URL后面请求参数的大小有限制 2.post请求 一般用于修改数据提交的数据在请求体中提交数据的大小没有限制 3.put请求 一般用于添加数据 4.delete请求 一般用于删除数据 5.一次完整的http请求过程 域名解析&#xff1a;使用DNS协议…

记录下学的性能优化

一、性能优化的指标和工具 1.1 谷歌浏览器 拿淘宝网站为例,可以看到当前网页的加载信息 这个是瀑布图,瀑布图有横向和纵向 横向是具体的加载数据,悬浮看详情列表,可以看出下载时最后一个步骤,在这之前会先排队,浏览器会对优先级进行安排,它会对高优先级的请求优先请求.然后通…

力扣刷题-二叉树-二叉树的高度与深度

二叉树最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 递归法 本题可以使用前序&#xff08;中左…

MindNode v5.0.1(思维导图软件)

思维导图软件哪个比较好呢&#xff1f;MindNode for mac一款功能简单&#xff0c;界面简洁&#xff0c;不用看教程都会用的思维导图软件。mindnode mac可随时随地记录自己的想法&#xff0c;让您从灵感入手&#xff0c;将奇思妙想铺陈在画布上&#xff0c;让一切井井有条。 Mi…

【Echarts】Echarts在不同屏幕分辨率下不同配置

目录 1、图表横坐标&#xff1a;分辨率低的情况下&#xff0c;刻度标签旋转的角度&#xff0c;在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠。2、图表图例&#xff1a;在 legend 文字超过6个字的时候对文字做裁剪并且开启 tooltip 参考文章&#xff1a;1、使…

C盘每次开机都会减少空间

如果C盘在没有安装任何大型软件的情况下&#xff0c;每次开机都会大幅度压缩C盘空间&#xff0c;这是由于电脑睡眠文件导致的&#xff0c;只需要一行命令即可&#xff1b; 以管理员身份打开命令提示符窗口&#xff01; 然后输入 powercfg -h off ok&#xff0c;马上就好了。…

MQ和redis的内部原理一些总结

首先&#xff0c;先知道内部原理&#xff1b;其次&#xff0c;就是查官方文档实战了。 但是如果不熟悉内部原理&#xff0c;那么仅仅只是安装官方文档&#xff0c;并不能排除跟踪问题和故障、预防风险等策略&#xff1b; 以下总结图解&#xff1a;&#xff08;mysql 8.0新增的…

CAPL编程 - 事件驱动

1 事件概述 CAPL是一种面向过程、由事件驱动的类C语言。 事件驱动针对于顺序执行&#xff0c;其区别如下&#xff1a; 顺序执行&#xff1a;顺序执行流程中&#xff0c;子例程或过程函数按照代码编写顺序逐句执行。 事件驱动&#xff1a;CAPL程序由事件驱动&#xff0c;工程…