- Excel 文件读取: Apache POI - 这是 Java 操作 Office 文件(如 .xlsx, .xls)的事实标准。
- 数据库连接: JDBC - Java 数据库连接,是 Java 访问数据库的基础 API。
- 数据库: MySQL - 作为示例数据库,但此方法同样适用于 Oracle, SQL Server, PostgreSQL 等。
第一步:环境准备
创建 Java 项目
创建一个标准的 Java Maven 项目,Maven 可以帮助我们非常方便地管理依赖。

添加 Maven 依赖
在你的 pom.xml 文件中,添加以下依赖:
<dependencies>
<!-- Apache POI 用于读取 Excel 文件 -->
<!-- 注意:poi 和 poi-ooxml 需要同时引入,poi-ooxml 支持 .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>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- (可选) Lombok 简化 Java Bean 代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
准备数据库和表
在 MySQL 数据库中创建一个目标表,我们假设要导入一个学生信息表。
创建数据库:
CREATE DATABASE IF NOT EXISTS demo_db; USE demo_db;
创建学生表:
我们假设 Excel 文件中有 id, name, age, email, enrollment_date 这几列。

CREATE TABLE IF NOT EXISTS `students` ( `id` INT PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `age` INT, `email` VARCHAR(150) UNIQUE, `enrollment_date` DATE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
第二步:准备 Excel 文件
在你的项目中创建一个 students.xlsx 文件,内容如下,并放在 src/main/resources 目录下(方便读取)。
| id | name | age | enrollment_date | |
|---|---|---|---|---|
| 1 | 张三 | 20 | zhangsan@example.com | 2025-09-01 |
| 2 | 李四 | 21 | lisi@example.com | 2025-09-01 |
| 3 | 王五 | 22 | wangwu@example.com | 2025-09-02 |
| 4 | 赵六 | 23 | zhaoliu@example.com | 2025-09-03 |
第三步:编写 Java 代码
我们将代码分为几个部分,使其结构清晰、易于维护。
数据库连接工具类 (DBUtil.java)
创建一个工具类来管理数据库连接和关闭资源,避免重复代码。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
// 数据库连接信息
private static final String URL = "jdbc:mysql://localhost:3306/demo_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();
}
}
}
}
实体类 (Student.java)
创建一个与数据库表结构对应的 Java Bean。

import lombok.Data;
import java.time.LocalDate;
@Data // Lombok 注解,自动生成 getter, setter, toString 等
public class Student {
private Integer id;
private String name;
private Integer age;
private String email;
private LocalDate enrollmentDate;
}
Excel 导入核心逻辑 (ExcelImportService.java)
这是实现导入功能的核心类。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ExcelImportService {
public List<Student> parseExcel(InputStream inputStream) throws Exception {
List<Student> studentList = new ArrayList<>();
Workbook workbook = new XSSFWorkbook(inputStream); // 使用 XSSFWorkbook 处理 .xlsx 文件
Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
// 假设第一行是标题,从第二行开始读取数据
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) {
continue; // 跳过空行
}
Student student = new Student();
// 读取单元格数据并处理
// 注意:getCell() 的第二个参数是 CellType,如果为空则返回 null
student.setName(getCellValueAsString(row.getCell(1))); // name
student.setAge(getCellValueAsInteger(row.getCell(2))); // age
// 处理日期类型
Cell dateCell = row.getCell(4);
if (dateCell != null && dateCell.getCellType() == CellType.NUMERIC) {
Date javaDate = dateCell.getDateCellValue();
LocalDate localDate = javaDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
student.setEnrollmentDate(localDate);
}
studentList.add(student);
}
workbook.close();
inputStream.close();
return studentList;
}
public void importToDatabase(List<Student> studentList) {
String sql = "INSERT INTO students (name, age, email, enrollment_date) VALUES (?, ?, ?, ?)";
// 使用 try-with-resources 确保 Connection 和 PreparedStatement 自动关闭
try (Connection conn = DBUtil.getConnection();
// 使用 PreparedStatement 防止 SQL 注入,并且可以批量执行
PreparedStatement pstmt = conn.prepareStatement(sql)) {
conn.setAutoCommit(false); // 开启事务
for (Student student : studentList) {
pstmt.setString(1, student.getName());
pstmt.setInt(2, student.getAge());
pstmt.setString(3, student.getEmail());
// 处理 LocalDate
if (student.getEnrollmentDate() != null) {
pstmt.setDate(4, java.sql.Date.valueOf(student.getEnrollmentDate()));
} else {
pstmt.setNull(4, java.sql.Types.DATE);
}
pstmt.addBatch(); // 添加到批量处理中
}
int[] results = pstmt.executeBatch(); // 执行批量插入
conn.commit(); // 提交事务
System.out.println("成功导入 " + results.length + " 条数据。");
} catch (SQLException e) {
System.err.println("数据库导入失败: " + e.getMessage());
e.printStackTrace();
}
}
// 辅助方法:将单元格内容转为字符串
private String getCellValueAsString(Cell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
// 如果是整数,直接转为 long 再转字符串,避免科学计数法
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue().toString();
} else {
return String.valueOf((long) cell.getNumericCellValue());
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
default:
return null;
}
}
// 辅助方法:将单元格内容转为整数
private Integer getCellValueAsInteger(Cell cell) {
String strValue = getCellValueAsString(cell);
if (strValue != null) {
try {
return Integer.parseInt(strValue);
} catch (NumberFormatException e) {
System.err.println("无法将 '" + strValue + "' 转换为整数。");
return null;
}
}
return null;
}
}
主程序入口 (Main.java)
创建一个主类来调用上面的服务。
import java.io.InputStream;
public class Main {
public static void main(String[] args) {
// 1. 从 resources 目录下加载 Excel 文件
// 使用 ClassLoader 加载资源,可以确保在 JAR 包中也能正确找到文件
InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xlsx");
if (inputStream == null) {
System.err.println("错误:在 resources 目录下未找到 students.xlsx 文件!");
return;
}
ExcelImportService service = new ExcelImportService();
try {
// 2. 解析 Excel 文件,得到学生列表
System.out.println("开始解析 Excel 文件...");
List<Student> studentList = service.parseExcel(inputStream);
System.out.println("解析完成,共读取 " + studentList.size() + " 条数据。");
// 3. 将数据导入数据库
System.out.println("开始导入数据库...");
service.importToDatabase(studentList);
System.out.println("导入完成!");
} catch (Exception e) {
System.err.println("处理过程中发生错误: " + e.getMessage());
e.printStackTrace();
}
}
}
第四步:运行和测试
- 确保数据库服务:你的 MySQL 服务必须正在运行。
- 修改配置:在
DBUtil.java中填入你的数据库用户名和密码。 - 放置文件:将
students.xlsx文件放入src/main/resources目录。 - 运行:运行
Main.java的main方法。
预期输出:
开始解析 Excel 文件...
解析完成,共读取 4 条数据。
开始导入数据库...
成功导入 4 条数据。
导入完成!
数据库验证: 查询数据库表,可以看到数据已经成功导入。
SELECT * FROM students;
高级主题和最佳实践
错误处理与数据回滚
上面的代码已经包含了基本的 try-catch 和事务管理 (conn.setAutoCommit(false) 和 conn.commit()),如果导入过程中某条数据出错,整个批次都会回滚,保证数据一致性。
性能优化:批量插入
我们使用了 PreparedStatement 的 addBatch() 和 executeBatch() 方法,这比逐条执行 INSERT 语句要快得多,因为它减少了与数据库的交互次数,对于大量数据(上万行),性能提升非常明显。
大文件处理与内存优化
XSSFWorkbook 会将整个 Excel 文件加载到内存中,如果文件非常大(几百MB甚至上GB),可能会导致 OutOfMemoryError。
解决方案:使用 SXSSFWorkbook (流式 API)
SXSSFWorkbook 是 POI 提供的用于处理大数据量的 API,它会在写入磁盘时生成临时文件,从而将内存占用降到最低。
修改 ExcelImportService 中的解析部分:
// ... 其他代码不变 // Workbook workbook = new XSSFWorkbook(inputStream); // 旧方式 // 新方式:SXSSFWorkbook SXSSFWorkbook workbook = new SXSSFWorkbook(new XSSFWorkbook(inputStream)); int windowSize = 100; // 内存中保留的行数,其余写入临时文件 workbook.setWindowSize(windowSize); // ... 后续代码不变
注意:SXSSFWorkbook 主要用于 写入 Excel,对于 读取 大文件,POI 目前没有完美的流式 API,对于读取,可以考虑分块读取或者使用更专业的库,但对于大多数业务场景,现代机器的内存足以处理几十MB的 Excel 文件。
日志记录
使用 SLF4J + Logback 等日志框架代替 System.out.println,可以更好地管理日志级别、输出格式和文件。
更健壮的单元格类型处理
getCellValueAsString 方法已经处理了多种类型,但在实际业务中,你可能需要更严格的校验,
- 年龄必须是正整数。
- Email 必须符合格式。
- 某些列不能为空。
你可以在 parseExcel 方法中增加校验逻辑,如果数据不合法,可以跳过该行或记录错误信息。
这个指南为你提供了一个从零开始、功能完整、结构清晰的 Java Excel 导入数据库的解决方案,你可以根据自己项目的具体需求进行修改和扩展。
