Spring Boot 集成 EasyExcel 輕松搞定百萬級數(shù)據(jù)導(dǎo)出
在企業(yè)級應(yīng)用開發(fā)中,數(shù)據(jù)導(dǎo)出是一個常見的需求。當(dāng)面臨百萬級數(shù)據(jù)導(dǎo)出時,傳統(tǒng)的數(shù)據(jù)導(dǎo)出方式往往會出現(xiàn)性能瓶頸,導(dǎo)致內(nèi)存溢出、導(dǎo)出速度慢等問題。Spring Boot 框架提供了靈活的開發(fā)環(huán)境,而 EasyExcel 是一款基于 Java 的高性能 Excel 操作庫,能夠高效地處理百萬級數(shù)據(jù)導(dǎo)出。本文將詳細(xì)介紹如何在 Spring Boot 項(xiàng)目中集成 EasyExcel,實(shí)現(xiàn)輕松搞定百萬級數(shù)據(jù)導(dǎo)出。
一、環(huán)境搭建
在開始之前,我們需要搭建一個 Spring Boot 項(xiàng)目。如果你還沒有搭建過 Spring Boot 項(xiàng)目,可以通過 Spring Initializr (https://start.spring.io/) 快速生成一個項(xiàng)目模板。然后,在項(xiàng)目的pom.xml文件中添加 EasyExcel 的依賴。
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- EasyExcel Starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
</dependencies>
接下來,我們需要配置 Excel 文件的路徑和文件名。在application.yml文件中添加以下配置:
excel:
path: ./excel
filename-prefix: data_
headers:
- id
- name
- age
- department
這里,我們配置了 Excel 文件的存儲路徑為項(xiàng)目根目錄下的excel文件夾,文件名前綴為data_,表頭信息包括id、name、age和department。
二、創(chuàng)建 Excel 模型類
為了將數(shù)據(jù)寫入 Excel 文件,我們需要創(chuàng)建一個 Excel 模型類。該類用于表示 Excel 文件中的數(shù)據(jù)結(jié)構(gòu),并使用@ExcelProperty注解標(biāo)記每個字段。
import com.alibaba.excel.annotation.ExcelProperty;
public class DataModel {
@ExcelProperty("ID")
private Long id;
@ExcelProperty("姓名")
private String name;
@ExcelProperty("年齡")
private Integer age;
@ExcelProperty("部門")
private String department;
// getter 和 setter 方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
}
在上面的代碼中,我們創(chuàng)建了一個DataModel類,包含了id、name、age和department四個字段。每個字段都使用@ExcelProperty注解標(biāo)記了對應(yīng)的表頭名稱。這樣,當(dāng)我們將數(shù)據(jù)寫入 Excel 文件時,字段值會按照注解中的名稱寫入對應(yīng)的列。
三、編寫數(shù)據(jù)導(dǎo)出服務(wù)類
數(shù)據(jù)導(dǎo)出服務(wù)類是實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出的核心部分。我們需要從數(shù)據(jù)庫中查詢數(shù)據(jù),并使用 EasyExcel 將數(shù)據(jù)寫入 Excel 文件。
import com.alibaba.excel.EasyExcel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ExportService {
@Value("${excel.path}")
private String excelPath;
@Value("${excel.filename-prefix}")
private String filenamePrefix;
public void exportData(List<DataModel> dataModels) {
// 生成文件名
String fileName = excelPath + "/" + filenamePrefix + System.currentTimeMillis() + ".xlsx";
// 使用 EasyExcel 寫入數(shù)據(jù)
EasyExcel.write(fileName, DataModel.class).sheet("Sheet1").doWrite(dataModels);
}
}
在上面的代碼中,我們創(chuàng)建了一個ExportService類,用于實(shí)現(xiàn)數(shù)據(jù)導(dǎo)出功能。該類注入了 Excel 文件的存儲路徑和文件名前綴,并提供了一個exportData方法。在exportData方法中,我們生成了一個唯一的文件名,然后使用 EasyExcel 的write方法將數(shù)據(jù)寫入 Excel 文件。write方法的參數(shù)包括文件名、數(shù)據(jù)模型類和工作表名稱,最后通過doWrite方法將數(shù)據(jù)寫入文件。
四、創(chuàng)建控制器類
為了方便用戶調(diào)用數(shù)據(jù)導(dǎo)出功能,我們需要創(chuàng)建一個控制器類,暴露 REST API 接口。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class ExportController {
@Autowired
private ExportService exportService;
@GetMapping("/export")
public String exportData() {
// 模擬從數(shù)據(jù)庫中查詢數(shù)據(jù)
List<DataModel> dataModels = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
DataModel dataModel = new DataModel();
dataModel.setId((long) i);
dataModel.setName("User" + i);
dataModel.setAge(30);
dataModel.setDepartment("Department" + i % 10);
dataModels.add(dataModel);
}
// 調(diào)用數(shù)據(jù)導(dǎo)出服務(wù)
exportService.exportData(dataModels);
return "Data exported successfully!";
}
}
在上面的代碼中,我們創(chuàng)建了一個ExportController類,用于處理數(shù)據(jù)導(dǎo)出的請求。該類注入了ExportService類,并提供了一個/export的 GET 請求接口。在exportData方法中,我們模擬了從數(shù)據(jù)庫中查詢數(shù)據(jù)的過程,生成了一個包含 1000000 條數(shù)據(jù)的列表。然后,調(diào)用exportService的數(shù)據(jù)導(dǎo)出功能,將數(shù)據(jù)寫入 Excel 文件。
五、性能優(yōu)化
在處理百萬級數(shù)據(jù)導(dǎo)出時,性能優(yōu)化是關(guān)鍵。EasyExcel 采用流式讀寫的方式,避免了大量數(shù)據(jù)對內(nèi)存的占用。我們可以通過以下方式進(jìn)行性能優(yōu)化:
1. 使用異步導(dǎo)出
對于大規(guī)模數(shù)據(jù)導(dǎo)出,可以使用異步處理的方式,避免主線程被阻塞。Spring Boot 提供了@Async注解,可以方便地實(shí)現(xiàn)異步導(dǎo)出。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ExportService {
@Value("${excel.path}")
private String excelPath;
@Value("${excel.filename-prefix}")
private String filenamePrefix;
@Async
public void exportData(List<DataModel> dataModels) {
// 生成文件名
String fileName = excelPath + "/" + filenamePrefix + System.currentTimeMillis() + ".xlsx";
// 使用 EasyExcel 寫入數(shù)據(jù)
EasyExcel.write(fileName, DataModel.class).sheet("Sheet1").doWrite(dataModels);
}
}
在上面的代碼中,我們在exportData方法上添加了@Async注解,將其標(biāo)記為異步方法。這樣,當(dāng)用戶調(diào)用數(shù)據(jù)導(dǎo)出接口時,數(shù)據(jù)導(dǎo)出將在后臺線程中執(zhí)行,不會阻塞主線程。
2. 分頁查詢數(shù)據(jù)
如果數(shù)據(jù)量非常大,一次性從數(shù)據(jù)庫中查詢所有數(shù)據(jù)可能會導(dǎo)致內(nèi)存不足。可以通過分頁查詢的方式,分批次加載數(shù)據(jù),并使用 EasyExcel 將數(shù)據(jù)分批次寫入 Excel 文件。
import com.alibaba.excel.EasyExcel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ExportService {
@Value("${excel.path}")
private String excelPath;
@Value("${excel.filename-prefix}")
private String filenamePrefix;
public void exportDataByPage() {
// 生成文件名
String fileName = excelPath + "/" + filenamePrefix + System.currentTimeMillis() + ".xlsx";
// 使用 EasyExcel 寫入數(shù)據(jù)
EasyExcel.write(fileName, DataModel.class).sheet("Sheet1").doWrite(data -> {
// 分頁查詢數(shù)據(jù)
for (int pageNum = 1; ; pageNum++) {
List<DataModel> dataModels = queryDataByPage(pageNum, 1000);
if (dataModels.isEmpty()) {
break;
}
data.write(dataModels);
}
});
}
private List<DataModel> queryDataByPage(int pageNum, int pageSize) {
// 模擬分頁查詢數(shù)據(jù)
List<DataModel> dataModels = new ArrayList<>();
int start = (pageNum - 1) * pageSize;
for (int i = start; i < start + pageSize; i++) {
DataModel dataModel = new DataModel();
dataModel.setId((long) i);
dataModel.setName("User" + i);
dataModel.setAge(30);
dataModel.setDepartment("Department" + i % 10);
dataModels.add(dataModel);
}
return dataModels;
}
}
在上面的代碼中,我們使用了分頁查詢的方式,每次查詢 1000 條數(shù)據(jù),并將數(shù)據(jù)分批次寫入 Excel 文件。這樣可以減少內(nèi)存的占用,并提高數(shù)據(jù)導(dǎo)出的性能。
六、測試與驗(yàn)證
在完成數(shù)據(jù)導(dǎo)出功能的開發(fā)后,需要進(jìn)行測試與驗(yàn)證。我們可以通過以下步驟進(jìn)行測試:
1. 準(zhǔn)備測試數(shù)據(jù)
首先,我們需要準(zhǔn)備一些測試數(shù)據(jù)??梢允褂脭?shù)據(jù)生成工具,或者手動創(chuàng)建一些數(shù)據(jù)。例如,可以創(chuàng)建一個包含 1000000 條記錄的數(shù)據(jù)庫表。
2. 調(diào)用數(shù)據(jù)導(dǎo)出接口
啟動 Spring Boot 應(yīng)用程序,并訪問/export接口,觸發(fā)數(shù)據(jù)導(dǎo)出功能。
3. 檢查導(dǎo)出結(jié)果
檢查生成的 Excel 文件是否包含所有數(shù)據(jù),并且數(shù)據(jù)格式是否正確??梢允褂?Excel 軟件打開文件,查看數(shù)據(jù)是否按照預(yù)期排列。
4. 測試性能
可以使用性能測試工具,如 JMeter 或 Gatling,對數(shù)據(jù)導(dǎo)出接口進(jìn)行測試,記錄導(dǎo)出時間、內(nèi)存占用、CPU 使用率等指標(biāo),評估數(shù)據(jù)導(dǎo)出功能的性能表現(xiàn)。