提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
拿来吧你
提示:粘贴即可用
SpringBoot 项目整合的导入导出接口。
一、接口说明
项目pom.xml 文件添加依赖,编写封装类即可。依赖、导入导出的封装类以及使用方法,以下文章都会一一列举,十分方便,已经经过测试,复制粘贴用起来吧。
二、依赖
提示:这里给的是关于导出导出的依赖。
代码如下(示例):
<!--实体类属性注解-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<!--导出导入-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.***/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.***mons</groupId>
<artifactId>***mons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<!--JSON-->
<dependency>
<groupId>***.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.21</version>
</dependency>
三、导出工具类
提示: 复制即可用
exportData()方法有三个参数:
- response :请求接口浏览器直接新开窗口下载导出的文件
- List dataList : 用户数据,比如导出User表数据,那么将从数据库查询的用户数据作为这第二个参数
- Class EntityClass : 实体类字节码,导出工具类直接根据实体类的字段和数据库的字段一一映射,将User.class作为这第三个参数。
代码如下(示例):
import org.apache.poi.ss.usermodel.*;
import javax.persistence.Column;
import javax.persistence.Table;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.***.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
public class ExportUtils {
public static <T> void exportData(HttpServletResponse response, List<T> dataList, Class<T> EntityClass) throws IOException {
// 创建Excel工作簿
Workbook workbook = WorkbookFactory.create(true);
// 创建工作表
// Sheet sheet = workbook.createSheet("数据表");
Sheet sheet = workbook.createSheet(EntityClass.getSimpleName());
// 设置标题行的字体样式
Font font = workbook.createFont();
font.setBold(true); // 将字体设置为加粗样式
// 获取表名
Table tableAnnotation = EntityClass.getAnnotation(Table.class);
// String tableName = EntityClass.getSimpleName(); // 获取的是UserDO(类的名字)
String tableName = "data";
if (tableAnnotation != null) {
tableName = tableAnnotation.name();
}
/**
* 创建标题行
* 1. 要根据实体类的 @Column(name = "姓名") 注解中的 name 值来设置标题行,
* 2. 你可以通过反射获取实体类的字段名称。
* 3. 可以使用 Class.getDeclaredFields() 方法来获取所有的字段,
* 4. 然后使用 Field.getAnnotation() 方法获取 @Column 注解,最后通过 name() 方法获取注解中设置的名称。
*/
Row headerRow = sheet.createRow(0);
Field[] fields = EntityClass.getDeclaredFields();
// 数据不为空,写数据,为空,下载模板,模板无序号
int columnIndex = 0;
for (Field field : fields) {
Column columnAnnotation = field.getAnnotation(Column.class);
if (columnAnnotation != null) {
String columnName = columnAnnotation.name();
// 排除id、createTime、updateTime、isDeleted等属性的导出
if (Objects.isNull(dataList)) {
if (!("id".equals(field.getName()) || "createTime".equals(field.getName())
|| "updateTime".equals(field.getName()) || "status".equals(field.getName())
|| "isDeleted".equals(field.getName()) || "icon".equals(field.getName()) )) {
Cell headerCell = headerRow.createCell(columnIndex);
headerCell.setCellValue(columnName);
// 应用字体样式到标题单元格
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setFont(font);
headerCell.setCellStyle(cellStyle);
columnIndex++;
}
} else {
Cell headerCell = headerRow.createCell(columnIndex);
headerCell.setCellValue(columnName);
// 应用字体样式到标题单元格
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.setFont(font);
headerCell.setCellStyle(cellStyle);
columnIndex++;
}
}
}
/**
* 1. 可以使用反射获取实体类的字段数量,
* 2. 然后在循环中根据字段数来创建单元格
*/
// 数据不为空-填充数据到Excel表格
// 数据为空,下载模板
if (Objects.nonNull(dataList)) {
int rowNumber = 1;
for (T data : dataList) {
Row row = sheet.createRow(rowNumber);
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
Column columnAnnotation = field.getAnnotation(Column.class);
if (columnAnnotation != null) {
Cell cell = row.createCell(i);
setCellValue(cell, data, field);
}
}
rowNumber++;
}
}
// 设置响应头信息
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
String encodedTableName = URLEncoder.encode(tableName, "UTF-8");
String filename = encodedTableName + ".xlsx";
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
// 将Excel写入响应输出流
workbook.write(response.getOutputStream());
// 关闭工作簿
workbook.close();
}
// 调用方法 setCellValue() 在 setCellValue() 方法中根据字段的类型来设置单元格的值
private static <T> void setCellValue(Cell cell, T data, Field field) {
field.setA***essible(true);
try {
Object value = field.get(data);
// 根据属性的类型来设置单元格的值
if (value instanceof Integer) {
cell.setCellValue((Integer) value);
} else if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value instanceof Date) {
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
DateTimeFormatter targetFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse(value.toString(), sourceFormatter);
String convertedTime = dateTime.format(targetFormatter);
cell.setCellValue(convertedTime);
} else if (value instanceof Double) {
cell.setCellValue(value.toString());
} else {
// 处理其他类型的值
}
} catch (IllegalA***essException e) {
// 处理异常情况
e.printStackTrace();
}
}
}
四、导入工具类
提示:导入即是上传文件xlsx文件,根据导入的数据转为Java语言,再做其他的操作,比如插入数据库。
参数解析:
- MultipartFile file : 上传到导入接口的文件
- Class entityClass : 比如是导入User类,则传入 User.class 作为参数
import org.apache.poi.ss.usermodel.*;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.Column;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
public class ImportUtil {
public static <T> List<T> importEntities(MultipartFile file, Class<T> entityClass) throws Exception {
// 创建Excel工作簿
Workbook workbook = WorkbookFactory.create(file.getInputStream());
// 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0);
// 获取标题行的列名
Row titleRow = sheet.getRow(0);
List<String> columnNames = getColumnNames(titleRow);
// 创建实体对象列表
List<T> entities = new ArrayList<>();
// 迭代每一行(跳过标题行)
Iterator<Row> iterator = sheet.iterator();
iterator.next(); // 跳过标题行
while (iterator.hasNext()) {
Row row = iterator.next();
// 读取单元格数据并创建实体对象
T entity = createEntityFromRow(row, columnNames, entityClass);
entities.add(entity);
}
// 关闭工作簿
workbook.close();
return entities;
}
// 获取列名
private static List<String> getColumnNames(Row row) {
List<String> columnNames = new ArrayList<>();
for (Cell cell : row) {
columnNames.add(getStringCellValue(cell));
}
return columnNames;
}
// 根据行数据创建实体对象
// 对
private static <T> T createEntityFromRow(Row row, List<String> columnNames, Class<T> entityClass) throws Exception {
T entity = entityClass.getDeclaredConstructor().newInstance();
Field[] fields = entityClass.getDeclaredFields();
for (int i = 0; i < columnNames.size(); i++) {
String columnName = columnNames.get(i);
String cellValue = getStringCellValue(row.getCell(i));
for (Field field : fields) {
field.setA***essible(true);
Column columnAnnotation = field.getAnnotation(Column.class);
if (columnAnnotation != null && columnAnnotation.name().equals(columnName)) {
setFieldValue(entity, field, cellValue);
break;
}
}
}
return entity;
}
// 获取单元格的字符串值
private static String getStringCellValue(Cell cell) {
if (cell == null) {
return null;
}
String cellValue;
// 根据单元格类型进行值转换
switch (cell.getCellType()) {
case STRING:
cellValue = cell.getStringCellValue();
break;
case NUMERIC:
// 判断是否为日期类型
if (DateUtil.isCellDateFormatted(cell)) {
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
cellValue = sdf.format(cell.getDateCellValue());
} else {
// 将数字类型转换为字符串类型
cell.setCellType(CellType.STRING);
cellValue = cell.getStringCellValue();
}
break;
case BOOLEAN:
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case FORMULA:
// 如果公式计算结果为字符串类型,则获取字符串值
if (cell.getCachedFormulaResultType() == CellType.STRING) {
cellValue = cell.getRichStringCellValue().getString();
}
// 如果公式计算结果为数字类型,则获取数字值并转换为字符串
else if (cell.getCachedFormulaResultType() == CellType.NUMERIC) {
cellValue = String.valueOf(cell.getNumeri***ellValue());
}
// 如果公式计算结果为布尔类型,则获取布尔值并转换为字符串
else if (cell.getCachedFormulaResultType() == CellType.BOOLEAN) {
cellValue = String.valueOf(cell.getBooleanCellValue());
} else {
cellValue = "";
}
break;
default:
cellValue = "";
break;
}
return cellValue;
}
// 设置实体类属性的值
private static <T> void setFieldValue(T entity, Field field, String cellValue) throws Exception {
field.setA***essible(true);
Class<?> fieldType = field.getType();
// 根据属性的类型进行赋值
if (fieldType == String.class) {
field.set(entity, cellValue);
} else if (fieldType == Integer.class) {
field.set(entity, Integer.valueOf(cellValue));
} else if (fieldType == Double.class) {
// field.set(entity, Double.valueOf(cellValue));
field.set(entity, cellValue);
}
// 在此处可以根据需要添加其他类型的赋值判断
else {
field.set(entity, null);
}
}
}
五、实体类的改造
这一步和导入导出息息相关,因为得根据实体类属性的注解来决定导出的列名是什么,表名是什么。以User类为例,看我操作。
- 主要看@Table(name = “用户信息表”)和@Column(name = “姓名”)
- @Data是lombok的,@TableName(“tb_user”)是mybatisplus指定数据库表的,这里只是解释一下,与导出导入无关,避免不知道的博友。
- 还有细心的朋友看见我实体类继承 BaseEntity.java,这是我写的一个公共实体类,主要写表中公共有的字段,避免在多张表重复写相同的属性。可以在下面了解一下。
import javax.persistence.Column;
import javax.persistence.Table;
@Data
@TableName("tb_user")
@Table(name = "用户信息表")
public class UserDO extends BaseEntity {
@Column(name = "姓名")
private String username;
@Column(name = "性别")
private String sex;
@Column(name = "密码")
private String password;
@Column(name = "邮箱")
private String email;
@Column(name = "地址")
private String address;
}
BaseEntity.java
继承这个类之后,User实体类就有id 的属性了,也有其他的字段,比如status, create_time等。这个类也要用@Column(name = “序号”) 标注。
import ***.baomidou.mybatisplus.annotation.IdType;
import ***.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import javax.persistence.Column;
import java.io.Serializable;
@Data
public class BaseEntity implements Serializable {
@TableId(type = IdType.AUTO)
@Column(name = "序号")
private Integer id;
}
六、控制层使用
准备工作做好之后,就是调用接口了,还是以User实体类为例,看我操作。
@RestController
@RequestMapping("/api/users")
@ResponseBody
public class UserController {
/**
* 下载数据模板
* 模块的原因是根据模板填写数据,上传的数据才不会出差错
* 为什么这样?具体原因你懂的
*/
@Transactional
@GetMapping("/download/template")
public void downloadTemplate(HttpServletResponse response) throws IOException {
// 调用接口,并传入空数据,在导出已经做出判断,
// 说明这个接口是下载模板,不会往表格里面写数据
ExportUtils.exportData(response, null , UserDO.class);
}
/**
* 导出
*/
@Transactional
@GetMapping("/export")
public void exportRole(HttpServletResponse response) throws IOException {
// 数据库获取数据,Mybatis-plus的方法,不知道的可以学习学习MP
List<UserDO> userDOList = userService.list();
// 调用导出工具类的方法,传入对应的参数,简单吧
ExportUtils.exportData(response, userDOList , UserDO.class);
}
/**
* 导入
*/
@Transactional
@PostMapping("/import")
public void importsEntity(@RequestParam("file") MultipartFile file) throws Exception {
// 调用ImportUtil工具类来获取实体对象列表
// 传入接口获取的文件和实体类,就可以获取到数据,简单吧
List<UserDO> entities = ImportUtil.importEntities(file, UserDO.class);
// 在这里处理导入数据的逻辑
for (UserDO entity : entities) {
// 执行对数据的操作,例如保存到数据库等
System.out.println("导入的数据:" + entity);
boolean save = userService.save(entity);
if (save) {
System.out.println("插入 " + entity + " 成功!");
} else {
System.out.println("插入 " + entity + " 失败!");
}
}
}
}
七、效果
数据库表
1. 下载模板
- 浏览器 URL 输入地址并回车
- 查看模板并没有id的属性打印,导出工具类判断了。测试正确。
2. 导出
- URL输入地址并回车
- 查看内容:数据正确,且与数据库表字段对应。而且注意一点,id只是在下载模板的时候不打印,导出我并没有设置不打印,看个人需求,你也可以自己改一下。
3. 导入
-
接下来用下载的模板进行写入数据,然后调用导入接口,获取数据。
-
数据这样测试,测试在后端接口是否能够正确接收空值,是否有错位。
-
清空控制台:方便查看打印的数据,无意义,只是表演需要
-
我这里使用 postman 工具请求:注意我框起来的点即可
-
铛铛铛铛:看这么几条,空的值都为空,对应的值都对应上
-
再看数据库:完美!!!扣 666
![在这里插入图片描述](https://img-blog.csdnimg.***/602c547646c04d7a9d66013738ce7362.png
总结
提示:觉得🐂B的扣个 666
惊不惊喜,意不意外!!!!