- Excel处理: Apache POI (支持
.xls和.xlsx格式) - 数据库连接: JDBC (以 MySQL 为例,其他数据库类似)
- 构建工具: Maven (用于管理依赖)
第一步:环境准备与依赖引入
你需要一个Java项目,并添加必要的依赖。

创建Maven项目
如果你使用IDE(如IntelliJ IDEA或Eclipse),可以直接创建一个Maven项目。
添加pom.xml依赖
在你的 pom.xml 文件中,添加以下依赖:
<dependencies>
<!-- 1. Apache POI: 用于操作Excel文件 -->
<!-- 注意:需要同时引入 poi, poi-ooxml 和 poi-scratchpad 来全面支持 .xls 和 .xlsx -->
<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>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version>
</dependency>
<!-- 2. MySQL Connector/J: 用于连接MySQL数据库 -->
<!-- 请根据你的数据库版本选择合适的版本 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 3. (可选但推荐) SLF4J 日志门面和 Logback 实现 -->
<!-- POI 5.x 版本开始强制要求 SLF4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
提示:
poi-ooxml依赖了ooxml-lite,它内部使用org.apache.xmlbeans来处理.xlsx格式,如果你的项目里没有冲突,这是最简单的方式。
第二步:准备数据库和Excel文件
创建数据库和表
假设我们要导入一个包含用户信息的Excel文件,我们先在MySQL中创建对应的表。

-- 创建数据库 CREATE DATABASE IF NOT EXISTS excel_import_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 使用数据库 USE excel_import_db; -- 创建用户表 CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '主键ID', `name` VARCHAR(50) NOT NULL COMMENT '姓名', `age` INT COMMENT '年龄', `email` VARCHAR(100) COMMENT '邮箱', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
准备Excel文件
在项目根目录下创建一个 files 文件夹,并放入一个名为 users.xlsx 的Excel文件,内容如下:
| 姓名 | 年龄 | 邮箱 |
|---|---|---|
| 张三 | 25 | zhangsan@example.com |
| 李四 | 30 | lisi@example.com |
| 王五 | 28 | wangwu@example.com |
| (空行) | ||
| 赵六 | 35 | zhaoliu@example.com |
注意:
- 第一行是标题行,我们将用它来映射数据库字段。
- 包含空行,这是测试代码健壮性的好方法。
- 编码: 确保Excel文件保存为
UTF-8编码,以避免中文乱码。
第三步:Java代码实现
我们将分步骤编写代码,使其清晰易懂。
数据库配置工具类
创建一个 DBUtil 类来管理数据库连接,避免代码重复。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
// 数据库连接信息
private static final String URL = "jdbc:mysql://localhost:3306/excel_import_db?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8";
private static final String USER = "root"; // 你的数据库用户名
private static final String PASSWORD = "your_password"; // 你的数据库密码
// 获取数据库连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
// 关闭资源
public static void close(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Excel导入核心逻辑
这是实现导入功能的核心类,我们将实现一个方法,接收Excel文件路径,然后逐行读取并插入数据库。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExcelImportService {
public void importExcel(String excelFilePath) {
Connection conn = null;
FileInputStream fis = null;
Workbook workbook = null;
try {
// 1. 获取数据库连接
conn = DBUtil.getConnection();
// 关闭自动提交,使用事务
conn.setAutoCommit(false);
// 2. 加载Excel文件
fis = new FileInputStream(excelFilePath);
// 根据文件版本选择不同的Workbook实现
// .xlsx -> XSSFWorkbook, .xls -> HSSFWorkbook
workbook = new XSSFWorkbook(fis);
// 3. 获取第一个工作表 (Sheet)
Sheet sheet = workbook.getSheetAt(0);
// 4. 获取第一行作为标题行,用于获取列名
Row headerRow = sheet.getRow(0);
if (headerRow == null) {
System.out.println("Excel文件为空或没有标题行!");
return;
}
// 5. 准备SQL语句 (使用PreparedStatement防止SQL注入)
// 我们使用 ? 占位符,后续会根据Excel列数动态设置
String sql = "INSERT INTO user (name, age, email, create_time) VALUES (?, ?, ?, ?)";
PreparedStatement pstmt = conn.prepareStatement(sql);
// 6. 遍历数据行 (从第二行开始,索引为1)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
// 跳过空行
if (row == null) {
System.out.println("第 " + (i + 1) + " 行是空行,已跳过。");
continue;
}
// 7. 获取单元格数据并处理
// 姓名
Cell nameCell = row.getCell(0);
String name = getCellValueAsString(nameCell);
if (name == null || name.trim().isEmpty()) {
System.out.println("第 " + (i + 1) + " 行姓名为空,已跳过。");
continue; // 如果关键字段为空,可以选择跳过或报错
}
// 年龄
Cell ageCell = row.getCell(1);
int age = 0;
try {
age = (int) getCellValueAsDouble(ageCell);
} catch (Exception e) {
System.out.println("第 " + (i + 1) + " 行年龄格式不正确,默认设为0。");
}
// 邮箱
Cell emailCell = row.getCell(2);
String email = getCellValueAsString(emailCell);
// 设置创建时间
String createTime = sdf.format(new Date());
// 8. 设置PreparedStatement参数并执行
pstmt.setString(1, name);
pstmt.setInt(2, age);
pstmt.setString(3, email);
pstmt.setString(4, createTime);
pstmt.addBatch(); // 添加到批量中
System.out.println("正在处理第 " + (i + 1) + " 行: " + name);
}
// 9. 执行批量插入
int[] result = pstmt.executeBatch();
System.out.println("成功插入 " + result.length + " 条数据。");
// 10. 提交事务
conn.commit();
System.out.println("数据导入成功!");
} catch (Exception e) {
// 发生异常时回滚事务
if (conn != null) {
try {
conn.rollback();
System.out.println("发生异常,事务已回滚。");
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 11. 关闭所有资源
DBUtil.close(conn);
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (workbook != null) {
try {
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将单元格的值转换为字符串
* 处理了数字、字符串、布尔值、日期等类型
*/
private String getCellValueAsString(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
// 如果是日期格式
if (DateUtil.isCellDateFormatted(cell)) {
return new SimpleDateFormat("yyyy-MM-dd").format(cell.getDateCellValue());
} else {
// 普通数字,避免科学计数法
return String.valueOf((long) cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
// 公式计算结果
try {
return String.valueOf(cell.getNumericCellValue());
} catch (Exception e) {
return cell.getStringCellValue();
}
case BLANK:
return null;
default:
return null;
}
}
/**
* 将单元格的值转换为Double,方便处理数字类型的字段
*/
private double getCellValueAsDouble(Cell cell) {
if (cell == null) {
return 0.0;
}
switch (cell.getCellType()) {
case NUMERIC:
return cell.getNumericCellValue();
case STRING:
try {
return Double.parseDouble(cell.getStringCellValue().trim());
} catch (NumberFormatException e) {
return 0.0;
}
case BOOLEAN:
return cell.getBooleanCellValue() ? 1.0 : 0.0;
default:
return 0.0;
}
}
}
主程序入口
创建一个 Main 类来运行导入服务。
public class Main {
public static void main(String[] args) {
// Excel文件路径
String excelPath = "files/users.xlsx";
ExcelImportService importService = new ExcelImportService();
importService.importExcel(excelPath);
}
}
第四步:运行与验证
- 运行程序: 运行
Main类的main方法。 - 观察控制台输出: 你会看到程序逐行处理数据,并打印出日志信息。
- 检查数据库: 连接到你的MySQL数据库,查询
user表,确认数据是否已正确导入。
第五步:高级技巧与最佳实践
上面的代码是基础版本,在实际项目中,你需要考虑更多。
使用事务确保数据一致性
代码中已经使用了 conn.setAutoCommit(false) 和 conn.commit()/conn.rollback(),这是非常重要的一步,它能保证要么所有数据都成功导入,要么在出错时全部回滚,避免数据部分导入导致的不一致。
批量插入 (addBatch / executeBatch)
对于大量数据(如上万行),使用 PreparedStatement 的批量插入功能可以极大地提高性能,它会将多个SQL语句打包在一起发送给数据库执行,减少了网络IO和数据库解析的开销。
错误处理与日志
- 空行/无效行处理: 代码中已经演示了如何跳过空行或关键字段为空的行。
- 数据类型转换:
getCellValueAsString方法处理了Excel中常见的各种数据类型,防止因类型不匹配导致的程序崩溃。 - 日志记录: 使用
SLF4J或Log4j等日志框架来记录操作日志,比System.out.println更专业,可以记录到文件,并控制日志级别。
内存优化
对于超大Excel文件(如几百MB甚至上GB),一次性将整个文件加载到内存(new XSSFWorkbook(fis))会导致 OutOfMemoryError。
解决方案:使用 SXSSFWorkbook (流API)
SXSSFWorkbook 是 POI 提供的用于处理大数据量的API,它基于 XSSFWorkbook,但会将不活跃的行数据写入临时文件(硬盘),从而保持内存在一个较低的水平。
示例代码片段 (使用 SXSSFWorkbook):
// 对于读取,SXSSFWorkbook 和 XSSFWorkbook 用法基本一致 // 主要区别在于写入超大文件时 // Workbook workbook = new SXSSFWorkbook(new XSSFWorkbook(fis)); // 不常用,读取时通常还是用XSSFWorkbook // 更常见的场景是生成超大Excel文件时使用SXSSFWorkbook
对于读取操作,如果文件真的超大,通常需要先检查文件大小,如果超过阈值,则提示用户分批处理或使用更专业的工具。
性能对比
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 逐条插入 | 代码简单,逻辑清晰 | 性能极差,N+1次IO | 仅用于测试或导入极少量数据(<10条) |
| 批量插入 | 性能良好,代码改动小 | 内存占用随行数线性增长 | 推荐使用,适用于大多数场景(<10万行) |
SXSSFWorkbook |
内存占用极低,可处理海量数据 | 代码稍复杂,依赖硬盘IO | 处理超大Excel文件(>10万行或文件>100MB) |
上传功能整合
在Web应用(如Spring Boot)中,通常需要一个文件上传接口,接收用户上传的Excel文件,然后调用上述导入服务。
Spring Boot 示例:
// Controller
@PostMapping("/upload")
public ResponseEntity<String> uploadExcel(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("请选择文件");
}
String originalFilename = file.getOriginalFilename();
// ... (做一些安全检查,如文件类型、大小等)
try {
// 将上传的文件保存到临时目录
String tempPath = "/tmp/" + originalFilename;
file.transferTo(new File(tempPath));
// 调用导入服务
excelImportService.importExcel(tempPath);
// 导入成功后,删除临时文件
Files.deleteIfExists(Paths.get(tempPath));
return ResponseEntity.ok("文件上传并导入成功!");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body("导入失败: " + e.getMessage());
}
}
通过以上步骤,你已经掌握了在Java中将Excel导入数据库的核心技术,关键点在于:
- 选择合适的工具:
Apache POI是处理Excel的行业标准。 - 使用JDBC和事务: 保证数据操作的原子性和一致性。
- 采用批量插入: 显著提升大数据量导入的性能。
- 健壮的错误处理: 确保程序能应对各种异常情况,如空行、格式错误等。
- 考虑性能边界: 对于超大文件,要采用
SXSSFWorkbook等内存优化策略。
希望这份详细的指南对你有帮助!
