springboot整合阿里easyexcel2.x實現海量數據excel導入導出demo


springboot整合阿里easyexcel實現海量數據excel導入導出。實現demo如下:

1.pom.xml導入依賴

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.6</version>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.2.0</version>
</dependency>
2.導出實體類
package com.zwj.easyexcel.data;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.util.Date;

/**
* @Author: zhengwj
* @Description: 特殊號碼 導出實體
* @Date: 2020/4/1 11:20
* @Version: 1.0
*/
@Data
public class ConfigFilterExport {

/**
* 特殊號碼主鍵
*/
@ExcelIgnore
private String filterPk;
/**
* 用戶號碼
*/
@ExcelProperty("用戶號碼")
private String filterNumber;
/**
* 用戶姓名
*/
@ExcelProperty("用戶姓名")
private String filterName;
/**
* 歸屬地
*/
@ExcelProperty("歸屬地")
private String filterLocation;
/**
* 號碼類型
*/
@ExcelProperty("號碼類型")
private String filterType;
/**
* 申請人
*/
@ExcelIgnore
private String filterApplicant;
/**
* 申請時間
*/
@ExcelIgnore
private Date filterApptime;
/**
* 申請原因
*/
@ExcelIgnore
private String filterReason;
/**
* 審核狀態
*/
@ExcelIgnore
private String filterStatus;
/**
* 是否有效
*/
@ExcelIgnore
private String filterIsenabled;
/**
* 創建人
*/
@ExcelIgnore
private String filterCreater;
/**
* 創建時間
*/
@ExcelIgnore
private Date filterCtime;
/**
* 更新人
*/
@ExcelIgnore
private String filterUpdater;
/**
* 更新時間
*/
@ExcelIgnore
private Date filterUtime;
}
3.導入實體類
package com.zwj.easyexcel.data;

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

import java.util.Date;

/**
* @Author: zhengwj
* @Description: 特殊號碼 導入實體(自定義解析列)
*
* @Date: 2020/4/1 11:20
* @Version: 1.0
*/
@Data
public class ConfigFilterImport {

/**
* 特殊號碼主鍵
*/
@ExcelIgnore
private String filterPk;
/**
* 用戶號碼
*/
@ExcelProperty("用戶號碼")
private String filterNumber;
/**
* 用戶姓名
*/
@ExcelProperty("用戶姓名")
private String filterName;
/**
* 歸屬地
*/
@ExcelProperty("歸屬地")
private String filterLocation;
/**
* 號碼類型
*/
@ExcelProperty("號碼類型")
private String filterType;
/**
* 申請人
*/
@ExcelIgnore
private String filterApplicant;
/**
* 申請時間
*/
@ExcelIgnore
private Date filterApptime;
/**
* 申請原因
*/
@ExcelIgnore
private String filterReason;
/**
* 審核狀態
*/
@ExcelIgnore
private String filterStatus;
/**
* 是否有效
*/
@ExcelIgnore
private String filterIsenabled;

}
3.導入監聽類
package com.zwj.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;

import com.alibaba.excel.event.AnalysisEventListener;
import com.zwj.easyexcel.dao.ConfigFilterDao;
import com.zwj.easyexcel.data.ConfigFilterImport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
* @Author: zhengwj
* @Description: 特殊號碼導入監聽類
* @Date: 2020/4/1 16:30
* @Version: 1.0
*/
// 不能被spring管理
public class ConfigFilterListener extends AnalysisEventListener<ConfigFilterImport> {

private static final Logger logger = LoggerFactory.getLogger(ConfigFilterListener.class);

private static final int BATCH_COUNT = 10000;

List<ConfigFilterImport> list = new ArrayList<>();

private ConfigFilterDao configFilterDao;
public ConfigFilterListener(ConfigFilterDao configFilterDao){
this.configFilterDao = configFilterDao;
}

/**
* 這個每一條數據解析都會來調用
*
* @param configFilter
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param analysisContext
*/
@Override
public void invoke(ConfigFilterImport configFilter, AnalysisContext analysisContext) {
list.add(configFilter);
// 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存儲完成清理 list
list.clear();
}
}

@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 這里也要保存數據,確保最后遺留的數據也存儲到數據庫
saveData();
logger.info("所有數據解析完成!");
}

/**
* 加上存儲數據庫
*/
private void saveData() {
logger.info("{}條數據,開始存儲數據庫!", list.size());
configFilterDao.save(list);
logger.info("存儲數據庫成功!");
}
}
4.導入導出實體的分頁查詢和批量新增方法
package com.zwj.easyexcel.dao;


import com.zwj.easyexcel.data.ConfigFilterExport;
import com.zwj.easyexcel.data.ConfigFilterImport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
* @Author: zhengwj
* @Description: 導入導出實體的分頁查詢和批量新增方法
*
* @Date: 2020/4/1 16:43
* @Version: 1.0
*/
@Repository
public class ConfigFilterDao {

private static final Logger logger = LoggerFactory.getLogger(ConfigFilterDao.class);

@Value("${spring.datasource.url}")
private String url ;
@Value("${spring.datasource.username}")
private String user;
@Value("${spring.datasource.password}")
private String password ;

/**
* 批量增加
* @param list 大批量數據使用原生jdbc
*/
//TODO
public void save(List<ConfigFilterImport> list) {
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, user, password);
String sql = "INSERT INTO TBL_CONFIG_FILTER............"; //插入sql
pstm = conn.prepareStatement(sql);
conn.setAutoCommit(false);
Long startTime = System.currentTimeMillis();
for(ConfigFilterImport dr : list){
pstm.setString(1, UUID.randomUUID().toString().replace("-", ""));
pstm.setString(2,"");
pstm.setString(2,"");
//.........
pstm.addBatch();
}
pstm.executeBatch();
conn.commit();
Long endTime = System.currentTimeMillis();
logger.info("用時:" + (endTime - startTime));
} catch (Exception e) {
logger.error("執行出錯{}",e.getMessage());
throw new RuntimeException(e);
} finally {
if (pstm != null) {
try {
pstm.close();
} catch (SQLException e) {
logger.error("執行出錯{}",e.getMessage());
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
logger.error("執行出錯{}",e.getMessage());
throw new RuntimeException(e);
}
}
}
}

//數據導出使用的分頁查詢方法 直接調用mybatis相應實體方法(sql優化)
public List<ConfigFilterExport> selectConfigFilterPage(Map<String, Object> param) {

//TODO
return null;
}
}
5.接口
package com.zwj.easyexcel.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.zwj.easyexcel.dao.ConfigFilterDao;
import com.zwj.easyexcel.data.ConfigFilterExport;
import com.zwj.easyexcel.data.ConfigFilterImport;
import com.zwj.easyexcel.listener.ConfigFilterListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* 數據量: 多
讀: 多
寫: 多
* 注:對於大批量數據前端使用ajax異步請求
* @author zwj
* @since 2020-03-31 14:57:45
*/
@RestController
@RequestMapping("/excel")
public class EasyExcelController {

private static final Logger logger = LoggerFactory.getLogger(EasyExcelController.class);

@Resource
private ConfigFilterDao configFilterDao;
/**
* 獲取特殊號碼導入模板
* @param request
* @param response
* @throws Exception
*/
@RequestMapping(value = "/getConfigFilterTemplate")
public void getConfigFilterTemplate(HttpServletRequest request, HttpServletResponse response) throws IOException{
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 這里URLEncoder.encode防止中文亂碼
String fileName = URLEncoder.encode("特殊號碼導入模板", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
List<ConfigFilterImport> list = new ArrayList<>();
EasyExcel.write(response.getOutputStream(), ConfigFilterImport.class).sheet("模板").doWrite(list);
}

/**
* 特殊號碼excel導入
* @param file
* @return
* @throws IOException
*/
@RequestMapping(value = { "/uploadConfigFilterExcel" }, method = { RequestMethod.POST })
public String uploadConfigFilterExcel(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), ConfigFilterImport.class, new ConfigFilterListener(configFilterDao)).sheet().doRead();
return "success";
}

/**
* 特殊號碼excel導出
* @param param
* @param response
* @throws IOException
*/
@RequestMapping(value = { "/downloadConfigFilter" }, method = { RequestMethod.GET })
public void download(@RequestParam Map<String, Object> param, HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");

String fileName = "ConfigFilter" + System.currentTimeMillis() + ".xlsx";
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 這里 需要指定寫用哪個class去寫
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ConfigFilterExport.class).build();
// 這里注意 如果同一個sheet只要創建一次
WriteSheet writeSheet = EasyExcel.writerSheet("特殊號碼").build();

int pageNumber = 1;
int pageSize = 10000;
int dataLength = pageSize;
List<ConfigFilterExport> data = null;
while (dataLength == pageSize){
// int startIndex = (pageNumber - 1) * pageSize;
param.put("pageNo", pageNumber);
param.put("pageSize", pageSize);
data=configFilterDao.selectConfigFilterPage(param); //分頁查詢
excelWriter.write(data, writeSheet);
if(null == data || data.isEmpty()){
break;
}
dataLength = data.size();
pageNumber++;
//寫數據
excelWriter.write(data, writeSheet);
}
excelWriter.finish();
}

}

支持多版本excel,實現方便,直接替換demo中的實體類即可。
框架導出導出效率非常之快,且不會內存溢出,功能優化在於sql,耗時主要在於sql執行,所以大批量數據時一定要使用原生jdbc操作。
完整代碼可在github上下載:https://github.com/wojozer/easyexcel-demo 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM