Java中大量數(shù)據(jù)Excel導(dǎo)入導(dǎo)出的完整實(shí)現(xiàn)過程
一、原理解析
1. Excel文件格式解析
- XLS格式:基于二進(jìn)制格式,使用Apache POI的HSSF組件處理
- XLSX格式:基于XML的壓縮格式,使用Apache POI的XSSF組件處理
2. 技術(shù)選型
- Apache POI:最常用的Java Excel處理庫
- EasyExcel:阿里巴巴開源,適合大數(shù)據(jù)量處理,內(nèi)存占用低
- JExcelAPI:輕量級(jí),但功能較少
3. 大數(shù)據(jù)量處理策略
- 流式讀取:逐行處理,避免內(nèi)存溢出
- 分批次處理:分頁讀取,批量入庫
- 異步處理:后臺(tái)線程處理,不阻塞主線程
二、實(shí)際應(yīng)用場景
1. 導(dǎo)入場景
- 用戶數(shù)據(jù)批量導(dǎo)入
- 產(chǎn)品目錄更新
- 訂單數(shù)據(jù)同步
- 財(cái)務(wù)報(bào)表導(dǎo)入
2. 導(dǎo)出場景
- 數(shù)據(jù)報(bào)表生成
- 數(shù)據(jù)備份
- 數(shù)據(jù)交換
- 批量打印
三、完整實(shí)現(xiàn)方案
項(xiàng)目結(jié)構(gòu)
excel-demo/ ├── pom.xml ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── example/ │ │ │ ├── ExcelApplication.java │ │ │ ├── controller/ │ │ │ │ └── ExcelController.java │ │ │ ├── service/ │ │ │ │ ├── ExcelService.java │ │ │ │ └── impl/ │ │ │ │ └── ExcelServiceImpl.java │ │ │ ├── dao/ │ │ │ │ └── UserDao.java │ │ │ ├── entity/ │ │ │ │ └── User.java │ │ │ ├── dto/ │ │ │ │ ├── ExcelImportResult.java │ │ │ │ └── ImportUserDTO.java │ │ │ └── listener/ │ │ │ └── UserExcelListener.java │ │ └── resources/ │ │ └── application.properties
1. Maven依賴 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>excel-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Apache POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 驗(yàn)證依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2. 實(shí)體類 (User.java)
package com.example.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
@Data
@TableName("t_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("user_name")
private String userName;
@TableField("email")
private String email;
@TableField("phone")
private String phone;
@TableField("age")
private Integer age;
@TableField("department")
private String department;
@TableField("salary")
private Double salary;
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
@TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
3. 導(dǎo)入DTO (ImportUserDTO.java)
package com.example.dto;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import javax.validation.constraints.*;
@Data
public class ImportUserDTO {
@ExcelProperty("用戶名")
@NotBlank(message = "用戶名不能為空")
@Size(min = 2, max = 20, message = "用戶名長度必須在2-20之間")
private String userName;
@ExcelProperty("郵箱")
@NotBlank(message = "郵箱不能為空")
@Email(message = "郵箱格式不正確")
private String email;
@ExcelProperty("手機(jī)號(hào)")
@NotBlank(message = "手機(jī)號(hào)不能為空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手機(jī)號(hào)格式不正確")
private String phone;
@ExcelProperty("年齡")
@NotNull(message = "年齡不能為空")
@Min(value = 18, message = "年齡必須大于等于18歲")
@Max(value = 65, message = "年齡必須小于等于65歲")
private Integer age;
@ExcelProperty("部門")
@NotBlank(message = "部門不能為空")
private String department;
@ExcelProperty("薪資")
@NotNull(message = "薪資不能為空")
@DecimalMin(value = "0.0", message = "薪資必須大于等于0")
private Double salary;
// 行號(hào),用于錯(cuò)誤定位
private Integer rowIndex;
// 導(dǎo)入結(jié)果信息
private String errorMsg;
// 是否驗(yàn)證通過
private Boolean valid = true;
}
4. 導(dǎo)入結(jié)果類 (ExcelImportResult.java)
package com.example.dto;
import lombok.Data;
import java.util.List;
@Data
public class ExcelImportResult<T> {
// 成功數(shù)量
private int successCount;
// 失敗數(shù)量
private int failCount;
// 總數(shù)量
private int totalCount;
// 失敗數(shù)據(jù)列表
private List<T> failDataList;
// 錯(cuò)誤文件下載路徑
private String errorFileUrl;
// 導(dǎo)入耗時(shí)(毫秒)
private Long costTime;
// 錯(cuò)誤信息匯總
private String errorSummary;
public ExcelImportResult() {
this.costTime = 0L;
}
public void calculate() {
this.totalCount = successCount + failCount;
}
}
5. Excel監(jiān)聽器 (UserExcelListener.java)
package com.example.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.example.dto.ImportUserDTO;
import com.example.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Slf4j
public class UserExcelListener implements ReadListener<ImportUserDTO> {
// 每隔1000條存儲(chǔ)數(shù)據(jù)庫,然后清理list,方便內(nèi)存回收
private static final int BATCH_COUNT = 1000;
// 緩存的數(shù)據(jù)
private List<ImportUserDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
private final ExcelService excelService;
// 驗(yàn)證器
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// 錯(cuò)誤數(shù)據(jù)列表
private final List<ImportUserDTO> errorDataList = new ArrayList<>();
// 成功計(jì)數(shù)
private int successCount = 0;
public UserExcelListener(ExcelService excelService) {
this.excelService = excelService;
}
@Override
public void invoke(ImportUserDTO data, AnalysisContext context) {
// 設(shè)置行號(hào)(從0開始,通常第0行是標(biāo)題)
Integer rowIndex = context.readRowHolder().getRowIndex() + 1;
data.setRowIndex(rowIndex);
// 數(shù)據(jù)驗(yàn)證
validateData(data);
if (data.getValid()) {
cachedDataList.add(data);
// 達(dá)到BATCH_COUNT,存儲(chǔ)一次數(shù)據(jù)庫
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
} else {
errorDataList.add(data);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 處理最后一批數(shù)據(jù)
if (!cachedDataList.isEmpty()) {
saveData();
}
log.info("Excel解析完成,成功導(dǎo)入{}條數(shù)據(jù),失敗{}條數(shù)據(jù)",
successCount, errorDataList.size());
}
private void validateData(ImportUserDTO data) {
Set<ConstraintViolation<ImportUserDTO>> violations = validator.validate(data);
if (!violations.isEmpty()) {
data.setValid(false);
StringBuilder errorMsg = new StringBuilder();
for (ConstraintViolation<ImportUserDTO> violation : violations) {
errorMsg.append(violation.getMessage()).append("; ");
}
data.setErrorMsg(errorMsg.toString());
}
}
private void saveData() {
try {
// 批量保存數(shù)據(jù)
int batchSuccessCount = excelService.batchSaveUsers(cachedDataList);
successCount += batchSuccessCount;
log.info("批量保存{}條數(shù)據(jù)成功", batchSuccessCount);
} catch (Exception e) {
log.error("批量保存數(shù)據(jù)失敗", e);
// 記錄失敗的數(shù)據(jù)
for (ImportUserDTO data : cachedDataList) {
data.setValid(false);
data.setErrorMsg("系統(tǒng)錯(cuò)誤: " + e.getMessage());
errorDataList.add(data);
}
}
}
public List<ImportUserDTO> getErrorDataList() {
return errorDataList;
}
public int getSuccessCount() {
return successCount;
}
}
6. Service層 (ExcelService.java & ExcelServiceImpl.java)
package com.example.service;
import com.example.dto.ExcelImportResult;
import com.example.dto.ImportUserDTO;
import com.example.entity.User;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface ExcelService {
/**
* 導(dǎo)入用戶數(shù)據(jù)
*/
ExcelImportResult<ImportUserDTO> importUsers(MultipartFile file);
/**
* 批量保存用戶
*/
int batchSaveUsers(List<ImportUserDTO> userList);
/**
* 導(dǎo)出用戶數(shù)據(jù)
*/
void exportUsers(HttpServletResponse response);
/**
* 導(dǎo)出錯(cuò)誤數(shù)據(jù)
*/
void exportErrorData(List<ImportUserDTO> errorDataList, HttpServletResponse response);
}
package com.example.service.impl;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.dao.UserDao;
import com.example.dto.ExcelImportResult;
import com.example.dto.ImportUserDTO;
import com.example.entity.User;
import com.example.listener.UserExcelListener;
import com.example.service.ExcelService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
@Slf4j
public class ExcelServiceImpl implements ExcelService {
@Autowired
private UserDao userDao;
@Override
@Transactional(rollbackFor = Exception.class)
public ExcelImportResult<ImportUserDTO> importUsers(MultipartFile file) {
long startTime = System.currentTimeMillis();
ExcelImportResult<ImportUserDTO> result = new ExcelImportResult<>();
try {
// 創(chuàng)建監(jiān)聽器
UserExcelListener listener = new UserExcelListener(this);
// 讀取Excel
EasyExcel.read(file.getInputStream(), ImportUserDTO.class, listener)
.sheet()
.headRowNumber(1) // 跳過標(biāo)題行
.doRead();
// 設(shè)置結(jié)果
result.setSuccessCount(listener.getSuccessCount());
result.setFailCount(listener.getErrorDataList().size());
result.setFailDataList(listener.getErrorDataList());
result.calculate();
// 如果有錯(cuò)誤數(shù)據(jù),生成錯(cuò)誤報(bào)告
if (!listener.getErrorDataList().isEmpty()) {
String errorSummary = generateErrorSummary(listener.getErrorDataList());
result.setErrorSummary(errorSummary);
}
} catch (IOException e) {
log.error("讀取Excel文件失敗", e);
result.setErrorSummary("文件讀取失敗: " + e.getMessage());
}
long endTime = System.currentTimeMillis();
result.setCostTime(endTime - startTime);
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int batchSaveUsers(List<ImportUserDTO> userList) {
if (userList == null || userList.isEmpty()) {
return 0;
}
List<User> users = new ArrayList<>();
for (ImportUserDTO dto : userList) {
User user = new User();
BeanUtils.copyProperties(dto, user);
user.setCreateTime(new Date());
user.setUpdateTime(new Date());
users.add(user);
}
// 批量插入
int count = userDao.batchInsert(users);
log.info("批量插入{}條用戶數(shù)據(jù)", count);
return count;
}
@Override
public void exportUsers(HttpServletResponse response) {
try {
// 查詢數(shù)據(jù)
List<User> userList = userDao.selectList(new QueryWrapper<>());
// 轉(zhuǎn)換為DTO
List<ImportUserDTO> exportData = new ArrayList<>();
for (User user : userList) {
ImportUserDTO dto = new ImportUserDTO();
BeanUtils.copyProperties(user, dto);
exportData.add(dto);
}
// 設(shè)置響應(yīng)頭
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("用戶數(shù)據(jù)導(dǎo)出", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 寫入Excel
EasyExcel.write(response.getOutputStream(), ImportUserDTO.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自動(dòng)列寬
.sheet("用戶數(shù)據(jù)")
.doWrite(exportData);
} catch (IOException e) {
log.error("導(dǎo)出Excel失敗", e);
throw new RuntimeException("導(dǎo)出失敗");
}
}
@Override
public void exportErrorData(List<ImportUserDTO> errorDataList, HttpServletResponse response) {
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("導(dǎo)入錯(cuò)誤數(shù)據(jù)", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), ImportUserDTO.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.sheet("錯(cuò)誤數(shù)據(jù)")
.doWrite(errorDataList);
} catch (IOException e) {
log.error("導(dǎo)出錯(cuò)誤數(shù)據(jù)失敗", e);
throw new RuntimeException("導(dǎo)出失敗");
}
}
/**
* 異步導(dǎo)入方法
*/
@Async
public CompletableFuture<ExcelImportResult<ImportUserDTO>> importUsersAsync(MultipartFile file) {
return CompletableFuture.completedFuture(importUsers(file));
}
private String generateErrorSummary(List<ImportUserDTO> errorDataList) {
StringBuilder summary = new StringBuilder();
summary.append("共發(fā)現(xiàn)").append(errorDataList.size()).append("條錯(cuò)誤數(shù)據(jù):\n");
for (ImportUserDTO data : errorDataList) {
summary.append("第").append(data.getRowIndex())
.append("行: ").append(data.getErrorMsg()).append("\n");
}
return summary.toString();
}
}
7. DAO層 (UserDao.java)
package com.example.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface UserDao extends BaseMapper<User> {
/**
* 批量插入用戶
*/
@Insert({
"<script>",
"INSERT INTO t_user(user_name, email, phone, age, department, salary, create_time, update_time) VALUES ",
"<foreach collection='list' item='item' separator=','>",
"(#{item.userName}, #{item.email}, #{item.phone}, #{item.age}, ",
"#{item.department}, #{item.salary}, #{item.createTime}, #{item.updateTime})",
"</foreach>",
"</script>"
})
int batchInsert(@Param("list") List<User> userList);
}
8. Controller層 (ExcelController.java)
package com.example.controller;
import com.example.dto.ExcelImportResult;
import com.example.dto.ImportUserDTO;
import com.example.service.ExcelService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/excel")
@Api(tags = "Excel導(dǎo)入導(dǎo)出管理")
@Slf4j
public class ExcelController {
@Autowired
private ExcelService excelService;
@PostMapping("/import/users")
@ApiOperation("導(dǎo)入用戶數(shù)據(jù)")
public ResponseEntity<Map<String, Object>> importUsers(
@RequestParam("file") MultipartFile file) {
Map<String, Object> result = new HashMap<>();
try {
// 檢查文件
if (file.isEmpty()) {
result.put("code", 400);
result.put("message", "文件不能為空");
return ResponseEntity.badRequest().body(result);
}
// 檢查文件類型
String fileName = file.getOriginalFilename();
if (fileName == null ||
(!fileName.toLowerCase().endsWith(".xls") &&
!fileName.toLowerCase().endsWith(".xlsx"))) {
result.put("code", 400);
result.put("message", "只支持Excel文件");
return ResponseEntity.badRequest().body(result);
}
// 執(zhí)行導(dǎo)入
ExcelImportResult<ImportUserDTO> importResult = excelService.importUsers(file);
result.put("code", 200);
result.put("message", "導(dǎo)入完成");
result.put("data", importResult);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("導(dǎo)入用戶數(shù)據(jù)失敗", e);
result.put("code", 500);
result.put("message", "導(dǎo)入失敗: " + e.getMessage());
return ResponseEntity.internalServerError().body(result);
}
}
@GetMapping("/export/users")
@ApiOperation("導(dǎo)出用戶數(shù)據(jù)")
public void exportUsers(HttpServletResponse response) {
try {
excelService.exportUsers(response);
} catch (Exception e) {
log.error("導(dǎo)出用戶數(shù)據(jù)失敗", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/export/errors")
@ApiOperation("導(dǎo)出錯(cuò)誤數(shù)據(jù)")
public void exportErrorData(
@RequestBody ExcelImportResult<ImportUserDTO> importResult,
HttpServletResponse response) {
try {
excelService.exportErrorData(importResult.getFailDataList(), response);
} catch (Exception e) {
log.error("導(dǎo)出錯(cuò)誤數(shù)據(jù)失敗", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/template/download")
@ApiOperation("下載導(dǎo)入模板")
public void downloadTemplate(HttpServletResponse response) {
try {
// 這里可以提供一個(gè)空模板或者帶說明的模板
excelService.exportUsers(response); // 暫時(shí)用導(dǎo)出方法,實(shí)際應(yīng)創(chuàng)建專門的模板
} catch (Exception e) {
log.error("下載模板失敗", e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
9. 啟動(dòng)類 (ExcelApplication.java)
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class ExcelApplication {
public static void main(String[] args) {
SpringApplication.run(ExcelApplication.class, args);
}
}
10. 配置文件 (application.properties)
# 服務(wù)器配置 server.port=8080 server.servlet.context-path=/excel-demo # 數(shù)據(jù)庫配置 spring.datasource.url=jdbc:mysql://localhost:3306/excel_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MyBatis配置 mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml mybatis-plus.type-aliases-package=com.example.entity mybatis-plus.configuration.map-underscore-to-camel-case=true mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # 上傳文件大小限制 spring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=100MB # 異步線程池配置 spring.task.execution.pool.core-size=10 spring.task.execution.pool.max-size=20 spring.task.execution.pool.queue-capacity=500 spring.task.execution.thread-name-prefix=excel-async- # 日志配置 logging.level.com.example=DEBUG
11. SQL建表語句
-- 創(chuàng)建數(shù)據(jù)庫
CREATE DATABASE IF NOT EXISTS excel_demo DEFAULT CHARACTER SET utf8mb4;
USE excel_demo;
-- 用戶表
CREATE TABLE IF NOT EXISTS t_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵ID',
user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
email VARCHAR(100) NOT NULL COMMENT '郵箱',
phone VARCHAR(20) NOT NULL COMMENT '手機(jī)號(hào)',
age INT COMMENT '年齡',
department VARCHAR(100) COMMENT '部門',
salary DECIMAL(10,2) COMMENT '薪資',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
INDEX idx_user_name (user_name),
INDEX idx_email (email),
INDEX idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';
-- 導(dǎo)入日志表(可選)
CREATE TABLE IF NOT EXISTS t_import_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主鍵ID',
file_name VARCHAR(255) COMMENT '文件名',
file_size BIGINT COMMENT '文件大小',
total_count INT COMMENT '總記錄數(shù)',
success_count INT COMMENT '成功數(shù)',
fail_count INT COMMENT '失敗數(shù)',
import_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '導(dǎo)入時(shí)間',
operator VARCHAR(50) COMMENT '操作人',
status TINYINT DEFAULT 1 COMMENT '狀態(tài):1-成功,0-失敗',
error_message TEXT COMMENT '錯(cuò)誤信息'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='導(dǎo)入日志表';
四、使用示例
1. 啟動(dòng)應(yīng)用
mvn spring-boot:run
2. 導(dǎo)入Excel
- 使用Postman或前端上傳Excel文件
- 文件格式要求:包含userName, email, phone, age, department, salary列
- 接口地址:POST http://localhost:8080/excel-demo/api/excel/import/users
3. 導(dǎo)出Excel
- 接口地址:GET http://localhost:8080/excel-demo/api/excel/export/users
五、優(yōu)化建議
1. 性能優(yōu)化
- 使用數(shù)據(jù)庫連接池
- 調(diào)整批量提交大小
- 使用多線程并行處理
- 添加Redis緩存
2. 功能增強(qiáng)
- 支持多種Excel格式
- 添加數(shù)據(jù)清洗功能
- 支持模板自定義
- 添加導(dǎo)入進(jìn)度查詢
3. 安全性考慮
- 文件類型驗(yàn)證
- 文件大小限制
- 病毒掃描
- 訪問權(quán)限控制
六、注意事項(xiàng)
- 內(nèi)存管理:大數(shù)據(jù)量導(dǎo)入時(shí),注意JVM內(nèi)存設(shè)置
- 事務(wù)控制:批量導(dǎo)入要考慮事務(wù)邊界
- 異常處理:完善的異常處理和日志記錄
- 數(shù)據(jù)驗(yàn)證:前端和后端雙重驗(yàn)證
- 用戶體驗(yàn):提供進(jìn)度反饋和錯(cuò)誤報(bào)告
到此這篇關(guān)于Java中大量數(shù)據(jù)Excel導(dǎo)入導(dǎo)出的文章就介紹到這了,更多相關(guān)Java大量數(shù)據(jù)Excel導(dǎo)入導(dǎo)出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
避免IDEA每次打開新項(xiàng)目都重復(fù)配置Maven的解決方案
每次打開新項(xiàng)目都要重新設(shè)置Maven路徑?每次導(dǎo)入工程都要手動(dòng)調(diào)整settings.xml?如果你也受夠了IDEA這種“健忘”行為,那么這篇文章就是為你準(zhǔn)備的!今天我們就來徹底解決這個(gè)問題,讓IDEA記住你的Maven配置,一勞永逸,需要的朋友可以參考下2025-04-04
shuffle的關(guān)鍵階段sort(Map端和Reduce端)源碼分析
今天小編就為大家分享一篇關(guān)于shuffle的關(guān)鍵階段sort(Map端和Reduce端)源碼分析,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例
這篇文章主要介紹了Java中使用數(shù)組實(shí)現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)實(shí)例,本文先是講解了實(shí)現(xiàn)棧至少應(yīng)該包括以下幾個(gè)方法等知識(shí),然后給出代碼實(shí)例,需要的朋友可以參考下2015-01-01
java Long類型轉(zhuǎn)為String類型的兩種方式及區(qū)別說明
這篇文章主要介紹了java Long類型轉(zhuǎn)為String類型的兩種方式及區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
部署在linux上的java服務(wù)老是掛掉問題排查日志
當(dāng)服務(wù)器掛掉時(shí),操作系統(tǒng)通常會(huì)記錄一些異常信息,檢查服務(wù)器的系統(tǒng)日志可以幫助定位問題所在,這篇文章主要介紹了部署在linux上的java服務(wù)老是掛掉問題排查日志的相關(guān)資料,需要的朋友可以參考下2025-10-10
教你使用java將excel數(shù)據(jù)導(dǎo)入MySQL
今天教大家如何使用Java將excel數(shù)據(jù)導(dǎo)入MySQL,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴呢很有幫助,需要的朋友可以參考下2021-05-05
rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題
這篇文章主要介紹了rabbitmq的消息持久化處理開啟,再關(guān)閉后,消費(fèi)者啟動(dòng)報(bào)錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Mybatis動(dòng)態(tài)SQL標(biāo)簽的使用
本文主要介紹了Mybatis動(dòng)態(tài)SQL標(biāo)簽的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05

