SpringBoot整合EasyExcel實(shí)現(xiàn)批量導(dǎo)入導(dǎo)出
不用Mybatis的原因就是因?yàn)樵诖罅繑?shù)據(jù)插入的時(shí)候jdbc性能比mybatis好
1. 首先分批讀取Excel中的數(shù)據(jù) 這一點(diǎn)EasyExcel有自己的解決方案
2.其次就是DB里插入,怎么去插入這20w條數(shù)據(jù) 當(dāng)然不能一條一條循環(huán),應(yīng)該批量插入20w條數(shù)據(jù)
3.使用JDBC+事務(wù)的批量操作將數(shù)據(jù)插入到數(shù)據(jù)庫
整個(gè)Demo連接,打開下載即可,包含數(shù)據(jù)庫表
為什么mybatis的foreach比JDBC的addBatch慢
ORM 框架開銷:MyBatis 的 foreach 操作涉及到將對(duì)象數(shù)據(jù)轉(zhuǎn)換為 SQL 語句的過程,在這個(gè)過程中需要進(jìn)行對(duì)象到 SQL 的映射、動(dòng)態(tài) SQL 的解析等操作,這些額外的操作會(huì)增加開銷。
數(shù)據(jù)庫連接管理:MyBatis 在執(zhí)行 foreach 操作時(shí),會(huì)頻繁地獲取和釋放數(shù)據(jù)庫連接,而數(shù)據(jù)庫連接的獲取和釋放是一個(gè)相對(duì)耗時(shí)的操作,特別在百萬級(jí)數(shù)據(jù)的情況下,這種開銷可能會(huì)積累導(dǎo)致性能下降。
SQL 語句生成:MyBatis 的 foreach 操作在執(zhí)行過程中會(huì)生成大量的 SQL 語句,這可能會(huì)導(dǎo)致數(shù)據(jù)庫的緩存失效、重新編譯查詢計(jì)劃等,從而影響性能。
批量插入優(yōu)化:JDBC 的 addBatch 可以直接利用底層數(shù)據(jù)庫的批量插入功能,而 MyBatis 的 foreach 操作在某些數(shù)據(jù)庫上可能不能充分利用數(shù)據(jù)庫的批量插入優(yōu)化。
1.引入依賴
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.42</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>2.Controller層
@Slf4j
@RestController
@RequestMapping("excel")
public class ExcelController {
@Autowired
private UserService userService;
//導(dǎo)出
@GetMapping("/exportExcel")
public String exportExcel(HttpServletRequest request, HttpServletResponse response){
String file="D:";
long startTime = System.currentTimeMillis();
log.debug("-------------開始插入-------------------");
userService.exportInspectionPlan(request,response);
return "ok";
}
//導(dǎo)入
@PostMapping("/importExcel")
public void importExcel(MultipartFile multipartFile) throws IOException {
if (multipartFile.isEmpty()) {
return;
}
// 這里 需要指定讀用哪個(gè)class去讀,然后讀取第一個(gè)sheet 文件流會(huì)自動(dòng)關(guān)閉
// 這里每次會(huì)讀取3000條數(shù)據(jù) 然后返回過來 直接調(diào)用使用數(shù)據(jù)就行
EasyExcel.read(multipartFile.getInputStream(), ExportPlanInformationVo.class,
new PageReadListener<ExportPlanInformationVo>(dataList -> {
for (ExportPlanInformationVo user : dataList) {
//將導(dǎo)入的數(shù)據(jù)用mybatisPlus一個(gè)個(gè)添加進(jìn)數(shù)據(jù)庫
System.out.println(user);
}
})).sheet("現(xiàn)場(chǎng)巡視計(jì)劃報(bào)表").doRead();
}
/**
* 1. 首先分批讀取Excel中的數(shù)據(jù)
* 這一點(diǎn)EasyExcel有自己的解決方案
*
* 2.其次就是DB里插入,怎么去插入這20w條數(shù)據(jù)
* 當(dāng)然不能一條一條循環(huán),應(yīng)該批量插入20w條數(shù)據(jù)
* 同樣不能選擇Mybatis的批量插入,因?yàn)樾实?
*
* 3.使用JDBC+事務(wù)的批量操作將數(shù)據(jù)插入到數(shù)據(jù)庫
* */
//批量導(dǎo)入
@PostMapping("/batchImportExcel")
public void batchImportExcel(MultipartFile file) throws IOException {
if (BeanUtil.isEmpty(file)){
log.debug("傳入的文件不能為空!");
return ;
}
if (!Objects.requireNonNull(file.getOriginalFilename()).endsWith("xls") && !file.getOriginalFilename().endsWith("xlsx")){
log.debug("請(qǐng)上傳Excel文件!");
return ;
}
CommonExportListenerDto commonExportListenerDto = new CommonExportListenerDto();
EasyExcel.read(file.getInputStream(),commonExportListenerDto).doReadAll();
}
}3.Service層
@Service
@Slf4j
public class UserService {
//
@Resource
private IndMapper indMapper;
//文件導(dǎo)出
public void exportInspectionPlan(HttpServletRequest request, HttpServletResponse response) {
//以上需要根據(jù)自己的業(yè)務(wù)去做數(shù)據(jù)處理 這里就不做展示
try {
//給文件命名
String fileName = "ExcelTest";
// 設(shè)置響應(yīng)頭
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("UTF-8");
// 設(shè)置防止中文名亂碼
fileName = URLEncoder.encode(fileName, "utf-8");
// 文件下載方式(附件下載還是在當(dāng)前瀏覽器打開)
response.setHeader("Content-disposition", "attachment;filename=" +
fileName + ".xlsx");
//向excel表格寫入數(shù)據(jù)
EasyExcel.write(response.getOutputStream(), ExportPlanInformationVo.class)
.sheet("下方導(dǎo)航")
.doWrite(getAll());
/*
//下載到指定路徑
String fileUrl = "D://ExcelTest.xlsx";
//向excel表格寫入數(shù)據(jù)
EasyExcel.write(fileUrl, ExportPlanInformationVo.class)
.sheet("下方導(dǎo)航")
.doWrite(getAll());
*/
} catch (Exception e) {
log.error("出現(xiàn)錯(cuò)誤 {}", e);
}
}
public List<ExportPlanInformationVo> getAll(){
//根據(jù)業(yè)務(wù)邏輯獲取數(shù)據(jù)
// List<ExportPlanInformationVo> all = indMapper.getAll();
return indMapper.getAll();
}
}4.Utils工具類
import java.sql.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
public class JDBCUtil {
private static String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false";
private static String username = "root";
private static String password = "196713";
private static String driverName = "com.mysql.jdbc.Driver";
/**
* 獲取連接對(duì)象
*
* @return 連接對(duì)象
*/
public static Connection getConnection() {
Connection conn = null;
try {
// 1. 注冊(cè)驅(qū)動(dòng)
Class.forName(driverName);
// 2. 獲取連接對(duì)象
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return conn;
}
/**
* 釋放資源
*
* @param connection 連接對(duì)象
* @param statement 預(yù)編譯執(zhí)行對(duì)象
* @param resultSet 結(jié)果集
*/
public static void releaseResources(Connection connection, PreparedStatement statement, ResultSet resultSet) {
// 釋放資源
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void insertBatch(List<Map<Object, Object>> dataList, String sql) {
Connection conn = null;
PreparedStatement pstm = null;
try {
conn = getConnection();
//如果需要開啟事務(wù)此處需要將自動(dòng)提交關(guān)閉
// conn.setAutoCommit(false);
//預(yù)編譯sql
pstm = (PreparedStatement) conn.prepareStatement(sql);
for (Map<Object, Object> map : dataList) {
//此處類型判斷不完整后續(xù)可以借鑒jdk自行封裝攔截器
for (int i = 1; i <= map.size(); i++) {
Object o = map.get(i-1);
if (BeanUtil.isEmpty(o)) {
pstm.setString(i, null);
continue;
}
if (o instanceof String) {
pstm.setString(i, o.toString());
continue;
}
if (o instanceof Integer) {
pstm.setInt(i, Integer.parseInt(o.toString()));
continue;
}
if (o instanceof LocalDateTime) {
pstm.setDate(i, new Date(System.currentTimeMillis()));
continue;
}
if (o instanceof Boolean) {
pstm.setBoolean(i, Boolean.parseBoolean(o.toString()));
}
}
//添加到同一個(gè)批處理中
pstm.addBatch();
}
//執(zhí)行批處理
pstm.executeBatch();
//如果需要開啟事務(wù)此處需要手動(dòng)提交事務(wù)
//conn.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}5.自定義監(jiān)聽器
@Data
@Slf4j
public class CommonExportListenerDto extends AnalysisEventListener<Map<Object, Object>> {
/**
* 表頭數(shù)據(jù)(存儲(chǔ)所有的表頭數(shù)據(jù))
*/
private List<Map<Integer, String>> headList = new ArrayList<>();
/*
* 數(shù)據(jù)體
*/
private List<Map<Object, Object>> dataList = new ArrayList<>();
/**
* 存儲(chǔ)全部表頭數(shù)據(jù)
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
headList.add(headMap);
}
/**
* 每一條數(shù)據(jù)解析都會(huì)來調(diào)用
* @param data
* @param context
*/
@Override
public void invoke(Map<Object, Object> data, AnalysisContext context) {
dataList.add(data);
if (dataList.size() >= 3) {
saveData();
// 存儲(chǔ)完成清理 list
dataList = ListUtils.newArrayListWithExpectedSize(2000);
}
}
/**
* 所有數(shù)據(jù)解析完成之后的操作
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();
}
private void saveData() {
log.info("{}條數(shù)據(jù),開始存儲(chǔ)數(shù)據(jù)庫!", dataList.size());
//批量導(dǎo)入
JDBCUtil.insertBatch(dataList,"INSERT INTO ind (a,b,c,d,e) VALUES(?,?,?,?,?);");
log.info("存儲(chǔ)數(shù)據(jù)庫成功!");
}
}6.實(shí)體類
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@HeadRowHeight(value = 30) // 頭部行高
@ContentRowHeight(value = 25) // 內(nèi)容行高
@ColumnWidth(value = 20) // 列寬
@HeadFontStyle(fontName = "宋體", fontHeightInPoints = 11)
public class ExportPlanInformationVo implements Serializable {
// 1. 如果不想某個(gè)字段在excel中出現(xiàn) 可以加 @ExcelIgnore注解
@ExcelProperty(value = "a")
private String a;
// @Dict(code = "inspectionType", fieldName = "inspectionTypeName")
@ExcelProperty(value = "b")
private String b;
@ExcelProperty(value = "c")
private String c;
@ExcelProperty(value = "d")
private String d;
@ExcelProperty(value = "c")
private String e;
}7.Mapper層
@Mapper
public interface IndMapper {
@Select("select * from ind")
List<ExportPlanInformationVo> getAll();
}以上就是SpringBoot整合EasyExcel實(shí)現(xiàn)批量導(dǎo)入導(dǎo)出的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot EasyExcel導(dǎo)入導(dǎo)出的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- SpringBoot中EasyExcel實(shí)現(xiàn)Excel文件的導(dǎo)入導(dǎo)出
- SpringBoot 導(dǎo)出數(shù)據(jù)生成excel文件返回方式
- SpringBoot?整合?EasyExcel?實(shí)現(xiàn)自由導(dǎo)入導(dǎo)出功能
- springboot實(shí)現(xiàn)excel表格導(dǎo)出幾種常見方法
- SpringBoot整合EasyExcel實(shí)現(xiàn)文件導(dǎo)入導(dǎo)出
- 使用VUE+SpringBoot+EasyExcel?整合導(dǎo)入導(dǎo)出數(shù)據(jù)的教程詳解
- SpringBoot+EasyPoi實(shí)現(xiàn)excel導(dǎo)出功能
- SpringBoot導(dǎo)出Excel的四種實(shí)現(xiàn)方式
- springboot實(shí)現(xiàn)對(duì)接poi 導(dǎo)出excel折線圖
相關(guān)文章
Java?C++題解leetcode902最大為N的數(shù)字組合數(shù)位DP
這篇文章主要為大家介紹了Java?C++題解leetcode902最大為N的數(shù)字組合數(shù)位DP,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
kafka運(yùn)維consumer-groups.sh消費(fèi)者組管理
這篇文章主要為大家介紹了kafka運(yùn)維consumer-groups.sh消費(fèi)者組管理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
基于GeoTools和SpringBoot的省域駕車最快路線規(guī)劃方法
本文探討基于GeoTools與SpringBoot的省域駕車最快路線規(guī)劃方法,解決復(fù)雜路況與地理因素影響,通過天地圖API計(jì)算并存儲(chǔ)距離數(shù)據(jù),展示湖南、新疆、黑龍江等地的路線成果,為交通與經(jīng)濟(jì)影響評(píng)估提供參考,感興趣的朋友快來一起學(xué)習(xí)吧2025-07-07
java數(shù)據(jù)結(jié)構(gòu)與算法之快速排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)與算法之快速排序,結(jié)合實(shí)例形式詳細(xì)分析了快速排序的原理、實(shí)現(xiàn)步驟、相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2017-05-05
微服務(wù)Spring?Boot?整合Redis?阻塞隊(duì)列實(shí)現(xiàn)異步秒殺下單思路詳解
這篇文章主要介紹了微服務(wù)Spring?Boot?整合Redis?阻塞隊(duì)列實(shí)現(xiàn)異步秒殺下單,使用阻塞隊(duì)列實(shí)現(xiàn)秒殺的優(yōu)化,采用異步秒殺完成下單的優(yōu)化,本文給大家分享詳細(xì)步驟及實(shí)現(xiàn)思路,需要的朋友可以參考下2022-10-10
SpringBoot前后端傳輸加密設(shè)計(jì)實(shí)現(xiàn)方案
這篇文章主要給大家介紹了關(guān)于SpringBoot前后端傳輸加密設(shè)計(jì)實(shí)現(xiàn)方案的相關(guān)資料,包括數(shù)據(jù)加密方案、解密傳輸數(shù)據(jù)實(shí)現(xiàn)方案和響應(yīng)數(shù)據(jù)加密實(shí)現(xiàn)方案,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11
Java連接MySQL數(shù)據(jù)庫并實(shí)現(xiàn)數(shù)據(jù)交互的示例
數(shù)據(jù)庫是非常重要的一種存儲(chǔ)格式,可以大大提高存儲(chǔ)效率,本文主要介紹了Java連接MySQL數(shù)據(jù)庫并實(shí)現(xiàn)數(shù)據(jù)交互的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
java利用Apache commons codec進(jìn)行MD5加密,BASE64加密解密,執(zhí)行系統(tǒng)命令
這篇文章主要介紹了java利用apache Commons包進(jìn)行MD5加密,BASE64加密解密與執(zhí)行系統(tǒng)命令希望對(duì)大家有用2017-12-12

