Java实现Excel批量导入数据库
场景说明
在实际开发中,经常需要解析Excel数据来插入数据库,而且通常会有一些要求,比如:全部校验成功才入库、校验成功入库,校验失败返回提示(总数、成功数、失败数、失败每行明细、导出失败文件明细…)
代码实现
数据库表
CREATE TABLE `forlan_student` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
1、pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>
</dependency>
2、文件模板
导入模板
public class ForlanStudentExcelModule {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
错误失败模板
@HeadStyle(horizontalAlignment = HorizontalAlignment.LEFT)
@ContentStyle(horizontalAlignment = HorizontalAlignment.LEFT)
public class ForlanStudentErrorExcelModule {
@ColumnWidth(20)
@ExcelProperty(value = "失败原因", index = 0)
private String excelOneLineErrorMsg;
@ColumnWidth(10)
@ExcelProperty(value = "姓名", index = 1)
private String name;
@ColumnWidth(10)
@ExcelProperty(value = "年龄", index = 2)
private Integer age;
public String getExcelOneLineErrorMsg() {
return excelOneLineErrorMsg;
}
public void setExcelOneLineErrorMsg(String excelOneLineErrorMsg) {
this.excelOneLineErrorMsg = excelOneLineErrorMsg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "ForlanStudentErrorExcelModule{" +
"excelOneLineErrorMsg='" + excelOneLineErrorMsg + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
3、Controller方法
@RestController
public class ExcelController {
@Autowired
private ForlanStudentService forlanStudentService;
@RequestMapping("/excel/import")
public String importFromExcel(@RequestParam(value = "file") MultipartFile param) {
// 校验文件类型
String fileName = param.getOriginalFilename();
if (StringUtils.isEmpty(fileName) || !(fileName.endsWith(".xlsx") || fileName.endsWith(".xls"))) {
return "文件后缀需为.xlsx或.xls";
}
return forlanStudentService.doImport(param);
}
}
4、Service方法
public interface ForlanStudentService {
String doImport(MultipartFile param);
}
5、主要实现逻辑
a、主方法
@Override
public String doImport(MultipartFile param) {
String result = "导入成功";
try (InputStream inputStream = param.getInputStream()) {
// 解析Excel对象流转成需要的对象
List<ForlanStudent> forlanStudentList = processExcel(inputStream);
// 最终入库数据
List<ForlanStudent> insertData = new ArrayList<>();
// 校验数据,并填充符合的数据
List<ForlanStudentErrorExcelModule> forlanStudentErrorExcelModule = checkDataAndFill(forlanStudentList, insertData);
if (!CollectionUtils.isEmpty(forlanStudentErrorExcelModule)) {
// 要求全部校验通过的话,这里可以直接return
// 需要的话,转成JSON返回,好看些
result = forlanStudentErrorExcelModule.toString();
// 可以生成错误文件,返回错误文件路径
// result = generateExceptionFile(forlanStudentErrorExcelModule);
}
if(!CollectionUtils.isEmpty(insertData)){
// 数据入库,根据自己需要写
forlanStudentDao.insertBatch(insertData);
}
} catch (Exception e) {
e.printStackTrace();
return "导入失败";
}
return result;
}
b、解析Excel数据转为List对象
private List<ForlanStudent> processExcel(InputStream inputStream) throws Exception {
List<ForlanStudent> forlanStudentList = new ArrayList<>();
Integer maxRows = 100;
// 导入模板表头
List<String> chineseHeader = Arrays.asList("姓名", "年龄");
// 0是表头
final int headerRows = 0;
try (Workbook workbook = WorkbookFactory.create(inputStream)) {
Sheet sheet = workbook.getSheetAt(0);
int totalRow = sheet.getLastRowNum();
if (totalRow == 0) {
throw new Exception("文件模板错误");
} else if (totalRow - headerRows > maxRows) {
throw new Exception(String.format("单次导入数据不能超过%s条", maxRows));
}
// 遍历每行
for (int rowIndex = 0; rowIndex <= totalRow; rowIndex++) {
Row currentRow = sheet.getRow(rowIndex);
if (currentRow == null) {
continue;
}
// 读取数据行
List<String> cellList = new ArrayList<>();
for (int columnIndex = 0; columnIndex <= 1; columnIndex++) {
Cell currentCell = currentRow.getCell(columnIndex);
cellList.add(formatCellValue(currentCell));
}
// 校验模板是否正确
if (rowIndex <= headerRows) {
if (rowIndex == 0 && !cellList.equals(chineseHeader)) {
throw new Exception("文件模板错误");
}
continue;
}
if (null != cellList && !cellList.isEmpty()) {
ForlanStudent forlanStudent = new ForlanStudent();
forlanStudent.setName(cellList.get(0));
forlanStudent.setAge(Integer.valueOf(cellList.get(1)));
forlanStudentList.add(forlanStudent);
}
}
} catch (Exception e) {
e.printStackTrace();
throw new Exception(e.getMessage());
}
return forlanStudentList;
}
public static String formatCellValue(Cell cell) {
if (cell == null) {
return "";
}
if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
} else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
if (HSSFDateUtil.isCellDateFormatted(cell)) {
double d = cell.getNumericCellValue();
Date date = HSSFDateUtil.getJavaDate(d);
return new SimpleDateFormat("yyyy/MM/dd HH:mm:ss\"").format(date);
} else {
// 强制将数字转字符串
DecimalFormat format = new DecimalFormat("0.00");
Number value = cell.getNumericCellValue();
String phone = format.format(value).replace(".00", "");
return String.valueOf(phone);
}
} else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
return String.valueOf(cell.getNumericCellValue());
} else {
try {
return cell.getStringCellValue() == null ? "" : cell.getStringCellValue().trim();
} catch (Exception e) {
return cell.toString() == null ? "" : cell.toString().trim();
}
}
}
c、校验数据,并填充入库数据、错误数据行
private List<ForlanStudentErrorExcelModule> checkDataAndFill(List<ForlanStudent> forlanStudentList, List<ForlanStudent> insertData) {
List<ForlanStudentErrorExcelModule> errorExcelModules = new ArrayList<>();
// 校验数据,支持拓展功能,比如,统计总量、成功数、失败数...
forlanStudentList.forEach(p -> {
if (StringUtils.isBlank(p.getName()) || null == p.getAge()) {
ForlanStudentErrorExcelModule forlanStudentErrorExcelModule = new ForlanStudentErrorExcelModule();
BeanUtils.copyProperties(p, forlanStudentErrorExcelModule);
forlanStudentErrorExcelModule.setExcelOneLineErrorMsg("请填写必填项");
errorExcelModules.add(forlanStudentErrorExcelModule);
return;
}
if (p.getAge() < 0) {
ForlanStudentErrorExcelModule forlanStudentErrorExcelModule = new ForlanStudentErrorExcelModule();
BeanUtils.copyProperties(p, forlanStudentErrorExcelModule);
forlanStudentErrorExcelModule.setExcelOneLineErrorMsg("年龄不能小于0");
errorExcelModules.add(forlanStudentErrorExcelModule);
return;
}
// 如果没有跳过,说明符合入库
ForlanStudent forlanStudent = new ForlanStudent();
BeanUtils.copyProperties(p, forlanStudent);
insertData.add(forlanStudent);
});
return errorExcelModules;
}
d、错误数据行原因生成文件
private String generateExceptionFile(List<ForlanStudentErrorExcelModule> forlanStudentErrorExcelModuleList) {
File file = new File("导入文件校验失败原因.xlsx");
ExcelWriter excelWriter = EasyExcel.write(file).build();
WriteSheet errorDataSheet = EasyExcel.writerSheet("导入失败原因").head(ForlanStudentErrorExcelModule.class).build();
excelWriter.write(forlanStudentErrorExcelModuleList, errorDataSheet);
excelWriter.finish();
// 可以上传到OOS或者七牛云...然后然后路径
return file.getPath();
}
e、复制对象内容
public class BeanUtils {
public static Map<String, BeanCopier> beanCopierMap = new HashMap<String, BeanCopier>();
public static void copyListProperties(List source, List desc, Class descClazz) {
for (Object o : source) {
try {
Object d = descClazz.newInstance();
copyProperties(o, d);
desc.add(d);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
public static void copyProperties(Object source, Object target) {
if (source != null) {
String beanKey = generateKey(source.getClass(), target.getClass());
if (!beanCopierMap.containsKey(beanKey)) {
BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
beanCopierMap.put(beanKey, copier);
}
beanCopierMap.get(beanKey).copy(source, target, null);
}
}
private static String generateKey(Class<?> cls1, Class<?> cls2) {
return cls1.toString() + cls2.toString();
}
}
总结
以上代码,校验文件格式、文件模板、导入数据限制、文本内容校验,支持全部校验成功才入库、部分校验成功入库,校验失败返回失败原因,导出失败原因
Java实现Excel批量导入数据库的更多相关文章
- JAVA实现Excel批量导入
一.模板下载: 先将模板放在项目WebRoot下的download文件夹下: /** * @Title: downloadFile * @Description: 模板下载 (网络地址) * @par ...
- C# 从Excel 批量导入数据库
最近遇到了关于 C# MVC 批量添加数据的问题,解决后就自己写了一个未完成的小Demo 不管什么编程语言都会提供操作Excel文件的方式,C#操作Excel主要有以下几种方式: 1.Excel 说 ...
- java实现excel表格导入数据库表
导入excel就是一个上传excel文件,然后获取excel文件数据,然后处理数据并插入到数据库的过程 一.上传excel 前端jsp页面,我的是index.jsp 在页面中我自己加入了一个下载上传文 ...
- excel批量导入数据库SQL server
思路: 第一是文件上传,可以参照Jakarta的FileUpload组件,用普通的Post也就行了.第二是Excel解析,用JSL或者POI都行第三是数据保存,这个应该简单吧,一个循环,一行对应一条数 ...
- Java实现数据批量导入mysql数据库
本文完全照搬别人的. 原文标题:Java实现数据批量导入数据库(优化速度-2种方法) 原文地址:https://blog.csdn.net/qy20115549/article/details/526 ...
- Java实现Excel数据批量导入数据库
Java实现Excel数据批量导入数据库 概述: 这个小工具类是工作中的一个小插曲哦,因为提数的时候需要跨数据库导数... 有的是需要从oracle导入mysql ,有的是从mysql导入oracle ...
- java实现文件批量导入导出实例(兼容xls,xlsx)
1.介绍 java实现文件的导入导出数据库,目前在大部分系统中是比较常见的功能了,今天写个小demo来理解其原理,没接触过的同学也可以看看参考下. 目前我所接触过的导入导出技术主要有POI和iRepo ...
- 订餐系统之Excel批量导入
批量导入现在基本已经成为各类系统的标配了,当前,我们订餐系统也不例外,什么商家呀.商品呀.优惠码之类的,都少不了.毕竟嘛,对非开发人员来说,看到Excel肯定比看到很多管理系统还是要亲切很多的.这里, ...
- 将Excle中的数据批量导入数据库
namespace 将Excle中的数据批量导入数据库{ class Program { static void Main(string[] args) { S ...
- 使用python将excel数据导入数据库
使用python将excel数据导入数据库 因为需要对数据处理,将excel数据导入到数据库,记录一下过程. 使用到的库:xlrd 和 pymysql (如果需要写到excel可以使用xlwt) 直接 ...
随机推荐
- Elastic: 创建一个 Elastic 邮件警报 - 7.7 发行版
文章转载自:https://blog.csdn.net/UbuntuTouch/article/details/106185321 总结: 1.elastic 免费版只有发送警报到一个索引或者到Ser ...
- MongoDB分片集群-Sharded Cluster
分片概念 分片(sharding)是一种跨多台机器分布数据的方法, MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作的部署. 换句话说:分片(sharding)是指将数据拆分,将其分散存在 ...
- Alertmanager结合Slack使用
Slack作为一款即时通讯工具,协作沟通主要通过Channel(平台)来完成,用户可以在企业中根据用途添加多个Channel,并且通过Channel来集成各种第三方工具. 例如,我们可以为监控建立一个 ...
- Vue子->父组件传值
父组件引入: Import Test from'' 父页面使用: <Test ref="test" @m1="m2"><Test/> 子 ...
- Codeforces Round #823 (Div. 2) A-D
比赛链接 A 题解 知识点:贪心. 对于一个轨道,要么一次性清理,要么一个一个清理.显然,如果行星个数大于直接清理的花费,那么选择直接清理,否则一个一个清理.即 \(\sum \min (c,cnt[ ...
- POJ1681 Painter's Problem(高斯消元)
题目看似与线性方程组无关,但可以通过建模转化为线性方程组的问题. 对于一块砖,刷两次是没有必要的,我们令x=1表示刷了一次,x=0没有刷,一共有n*n个,所以相当于有n*n个未知量x. 定义aij表示 ...
- 如何用Virtualbox搭建一个虚拟机
序言 各位好啊,我是会编程的蜗牛,作为java开发者,我们肯定会接触Linux服务器,除了使用云服务搭建Linux服务器外,我们一般也可以在自己的电脑上安装虚拟机来搭建Linux服务器用于各种功能的验 ...
- Ubuntu安装错误 server64 busybox-initramfs安装失败
因为想试试在Linux系统上爆破 所以安装了一下Ubuntu.第一次安装包报了个server64 busybox-initramfs.在安装系统那边.我一直还以为是我磁盘分配错了. 后来在网上找了资料 ...
- Vue学习之--------组件嵌套以及VueComponent的讲解(代码实现)(2022/7/23)
欢迎加入刚建立的社区:http://t.csdn.cn/Q52km 加入社区的好处: 1.专栏更加明确.便于学习 2.覆盖的知识点更多.便于发散学习 3.大家共同学习进步 3.不定时的发现金红包(不多 ...
- Vue学习之--------计算属性(2022/7/9)
文章目录 1.计算属性 1.1 计算属性实现 1.1.1 基础知识 1.1.2 代码实例 1.1.3 测试效果 1.2 计算属性简写 1.2.1 简写代码 1.3 使用插值语法实现 1.3.1 代码实 ...