- EasyExcel: 阿里巴巴开源,API 简单,性能优秀,内存占用低,是目前推荐的首选。
- Apache POI: 老牌 Java 操作 Office 文件的库,功能强大,但在处理大文件时内存消耗较高。
本文将以 EasyExcel 为核心进行讲解,因为它更符合现代应用的需求。

整体步骤概览
- 环境准备: 创建项目,添加必要的依赖。
- 准备数据库和表: 创建目标数据表。
- 准备 Excel 文件: 创建一个示例
.xlsx文件。 - 编写 Java 代码:
- 定义一个与 Excel 行数据对应的 Java 实体类。
- 创建一个监听器,用于在读取 Excel 时逐行处理数据。
- 编写主程序,启动 Excel 读取,并通过监听器将数据写入数据库。
- 运行与验证: 执行代码,检查数据库中的数据。
第一步:环境准备
创建 Maven 项目
如果你使用的是 IDE (如 IntelliJ IDEA 或 Eclipse),可以直接创建一个 Maven 项目。
添加依赖
在 pom.xml 文件中添加以下依赖,你需要 EasyExcel、JDBC 驱动(以 MySQL 为例)以及一个数据库连接池(如 HikariCP,性能优秀)。
<dependencies>
<!-- EasyExcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version> <!-- 请使用最新版本 -->
</dependency>
<!-- MySQL JDBC Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version> <!-- 请根据你的MySQL版本选择 -->
</dependency>
<!-- HikariCP 高性能数据库连接池 -->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version> <!-- 请使用最新版本 -->
</dependency>
<!-- Lombok (可选,用于简化实体类代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
</dependencies>
第二步:准备数据库和表
假设我们要导入的是用户信息,创建一个 user 表。
CREATE DATABASE IF NOT EXISTS demo_db; USE demo_db; CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL COMMENT '姓名', `gender` VARCHAR(10) DEFAULT NULL COMMENT '性别', `age` INT DEFAULT NULL COMMENT '年龄', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
第三步:准备 Excel 文件
创建一个名为 users.xlsx 的 Excel 文件,内容如下。第一行必须是表头。

| 姓名 | 性别 | 年龄 |
|---|---|---|
| 张三 | 男 | 25 |
| 李四 | 女 | 30 |
| 王五 | 男 | 28 |
| 赵六 | 女 | 35 |
第四步:编写 Java 代码
我们将代码分为几个部分,结构清晰。
实体类
创建一个 User.java 类,其字段名需要与 Excel 的表头列名完全一致(不区分大小写,但推荐保持一致),EasyExcel 会自动根据列名进行映射。
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
@Data // Lombok 注解,自动生成 getter, setter, toString 等
public class User {
// ExcelProperty 的 value 值必须与 Excel 表头列名一致
@ExcelProperty("姓名")
private String name;
@ExcelProperty("性别")
private String gender;
@ExcelProperty("年龄")
private Integer age;
// 数据库中的创建时间,Excel 文件中没有,我们可以在代码中设置
private Date createTime;
}
数据库连接工具类
创建一个 JdbcUtils.java 用于管理数据库连接。
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcUtils {
private static HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8");
config.setUsername("root"); // 你的数据库用户名
config.setPassword("your_password"); // 你的数据库密码
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 连接池配置 (可选)
config.setMaximumPoolSize(10);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000); // 30 seconds
config.setIdleTimeout(600000); // 10 minutes
config.setMaxLifetime(1800000); // 30 minutes
dataSource = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void closeConnection(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Excel 监听器
这是整个流程的核心,监听器会在 EasyExcel 读取每一行数据时被调用,我们在这里实现数据库的写入逻辑。

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
// 有参构造函数中传入 Connection,确保每次导入都使用同一个连接
public class UserExcelListener extends AnalysisEventListener<User> {
private static final Logger LOGGER = LoggerFactory.getLogger(UserExcelListener.class);
private final Connection connection;
// 使用 PreparedStatement 批量插入,提高性能
private final PreparedStatement preparedStatement;
// 每批处理的数量
private static final int BATCH_SIZE = 100;
// 用于暂存数据的列表
private List<User> cachedDataList = new ArrayList<>(BATCH_SIZE);
public UserExcelListener(Connection connection) throws SQLException {
this.connection = connection;
// 关闭自动提交,开启事务
connection.setAutoCommit(false);
// 准备 SQL 语句
String sql = "INSERT INTO user (name, gender, age, create_time) VALUES (?, ?, ?, ?)";
this.preparedStatement = connection.prepareStatement(sql);
}
/**
* 这个方法会逐行调用
*/
@Override
public void invoke(User user, AnalysisContext context) {
LOGGER.info("解析到一条数据: {}", user);
// 设置创建时间
user.setCreateTime(new Date());
cachedDataList.add(user);
// 达到 BATCH_SIZE 时,触发批量存储
if (cachedDataList.size() >= BATCH_SIZE) {
saveData();
cachedDataList.clear();
}
}
/**
* 所有数据解析完成后调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 确保剩余的数据也被保存
if (!cachedDataList.isEmpty()) {
saveData();
}
LOGGER.info("所有数据解析完成!");
try {
// 提交事务
connection.commit();
} catch (SQLException e) {
try {
// 发生异常,回滚事务
connection.rollback();
LOGGER.error("事务回滚", e);
} catch (SQLException ex) {
LOGGER.error("回滚失败", ex);
}
} finally {
JdbcUtils.closeConnection(connection);
}
}
/**
* 批量保存数据
*/
private void saveData() {
try {
for (User user : cachedDataList) {
preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getGender());
preparedStatement.setInt(3, user.getAge());
preparedStatement.setTimestamp(4, new java.sql.Timestamp(user.getCreateTime().getTime()));
preparedStatement.addBatch();
}
// 执行批量更新
int[] results = preparedStatement.executeBatch();
LOGGER.info("成功插入 {} 条数据", results.length);
} catch (SQLException e) {
LOGGER.error("批量保存数据失败", e);
// 如果批量插入失败,可以尝试单条插入或直接抛出异常
// 这里简单处理,打印日志
}
}
}
主程序
编写 Main.java 来启动整个导入过程。
import com.alibaba.excel.EasyExcel;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) {
String fileName = "D:/path/to/your/users.xlsx"; // 替换成你的 Excel 文件路径
// 1. 获取数据库连接
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
System.out.println("数据库连接成功!");
// 2. 构建 Excel 监听器,并将连接传入
UserExcelListener listener = new UserExcelListener(connection);
// 3. 读取 Excel 文件
// EasyExcel.read(fileName, User.class, listener).sheet().doRead();
// 如果文件在 resources 目录下,可以这样读取
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("users.xlsx");
if (inputStream == null) {
System.err.println("未找到 Excel 文件,请检查路径是否正确!");
return;
}
EasyExcel.read(inputStream, User.class, listener).sheet().doRead();
System.out.println("Excel 导入数据库完成!");
} catch (SQLException e) {
System.err.println("数据库操作失败: " + e.getMessage());
e.printStackTrace();
}
}
}
第五步:运行与验证
- 确保路径正确: 修改
Main.java中fileName的值为你的users.xlsx的绝对路径,或者将users.xlsx文件放到src/main/resources目录下,并使用getResourceAsStream方式读取(如示例代码所示)。 - 配置数据库信息: 修改
JdbcUtils.java中的数据库用户名、密码等信息。 - 运行: 运行
Main.java的main方法。 - 验证: 查看你的 MySQL 数据库
demo_db中的user表,数据应该已经被成功导入。
你还会在控制台看到类似以下的日志输出:
数据库连接成功!
解析到一条数据: User(name=张三, gender=男, age=25, createTime=...)
解析到一条数据: User(name=李四, gender=女, age=30, createTime=...)
...
成功插入 100 条数据
所有数据解析完成!
Excel 导入数据库完成!
最佳实践与注意事项
- 大文件处理: EasyExcel 采用 SAX 模式(事件驱动)读取,不会一次性将整个 Excel 文件加载到内存,因此可以处理非常大的文件(如几百兆甚至上 G),而 Apache POI 的 XSSF 模式是 DOM 模式,会将文件全部读入内存,容易导致 OOM(内存溢出)。
- 事务管理: 在监听器中,我们手动管理了事务(
connection.setAutoCommit(false),connection.commit()/rollback()),这非常重要,可以保证一批次的数据要么全部成功,要么全部失败,保证数据一致性。 - 批量插入: 使用
PreparedStatement的addBatch()和executeBatch()方法,可以极大地提升数据库写入性能,减少网络 I/O 次数。 - 数据校验: 在实际应用中,你可能需要对 Excel 中的数据进行校验(如非空、格式、长度等),可以在
invoke方法中增加校验逻辑,如果数据不合法,可以跳过该行或记录错误信息。 - 错误处理: 增加更完善的异常处理和日志记录,以便在导入失败时能够快速定位问题。
- 性能优化:
- 调整 BATCH_SIZE:
BATCH_SIZE是一个重要的性能调优参数,你可以根据数据量和服务器性能进行调整,通常在 100 到 1000 之间。 - 使用连接池: 我们已经使用了 HikariCP,这是目前性能最好的连接池之一。
- 调整 BATCH_SIZE:
- 复杂表头: Excel 表头有多行或复杂的合并单元格,EasyExcel 也支持,需要使用
Head相关的注解和配置,相对复杂一些。
