Java中将Excel文件导入数据库是一个常见的需求,通常涉及使用Apache POI库读取Excel数据,并通过JDBC连接数据库进行插入操作,以下是详细的实现步骤和示例代码:
准备工作
-
环境配置
- 确保已安装Java JDK(建议版本≥8)、IDE工具(如IntelliJ IDEA或Eclipse),以及目标数据库管理系统(例如MySQL)。
- 如果使用Maven管理项目依赖,需在
pom.xml中添加以下库:<!-Apache POI用于处理Excel --> <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>
-
创建测试用的Excel文件:例如名为
data.xlsx的文件,内容包含表头和若干行记录,示例结构如下:
| ID | Name | Age |
|—-|———|—–|
| 1 | Alice | 30 |
| 2 | Bob | 25 | -
设计数据库表结构:根据Excel的列名创建对应的数据库表,在MySQL中执行:
CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(255), age INT );
核心实现步骤
读取Excel文件
使用Apache POI解析Excel文件,逐行提取数据,关键点包括:跳过表头行、处理不同类型单元格(字符串/数字等)、避免空值异常,以下是典型代码片段:
import org.apache.poi.ss.usermodel.;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
public class ExcelReader {
public List<List<Object>> readExcel(String filePath) throws IOException {
List<List<Object>> dataList = new ArrayList<>();
try (FileInputStream fis = new FileInputStream(filePath);
Workbook workbook = new XSSFWorkbook(fis)) {
Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
for (Row row : sheet) {
if (row.getRowNum() == 0) continue; // 跳过表头
List<Object> rowData = new ArrayList<>();
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING:
rowData.add(cell.getStringCellValue());
break;
case NUMERIC:
rowData.add(cell.getNumericCellValue());
break;
default:
rowData.add(null); // 其他类型暂存为null
}
}
dataList.add(rowData);
}
}
return dataList;
}
}
注意:上述代码通过判断单元格类型动态适配不同格式的数据,确保数值型字段不会被错误转换为字符串。
连接数据库并插入数据
通过JDBC建立连接后,利用PreparedStatement批量执行SQL语句以提高效率,推荐使用事务管理保证操作的原子性:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;
public class DatabaseInserter {
private static final String URL = "jdbc:mysql://localhost:3306/your_database";
private static final String USERNAME = "your_username";
private static final String PASSWORD = "your_password";
public void insertData(List<List<Object>> rows) throws SQLException {
try (Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
String sql = "INSERT INTO users (id, name, age) VALUES (?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (List<Object> row : rows) {
pstmt.setInt(1, (Integer) row.get(0)); // ID列为整数类型
pstmt.setString(2, (String) row.get(1)); // Name列为字符串类型
pstmt.setInt(3, (Integer) row.get(2)); // Age列为整数类型
pstmt.addBatch(); // 添加到批处理队列
}
pstmt.executeBatch(); // 一次性执行所有批次操作
}
}
}
}
优化点:①使用批处理(Batch Processing)减少网络交互次数;②显式指定参数类型防止SQL注入攻击;③自动提交事务由JDBC默认机制控制。
整合流程
将上述模块组合成完整的导入程序:
public class MainApp {
public static void main(String[] args) {
String excelPath = "src/main/resources/data.xlsx";
ExcelReader reader = new ExcelReader();
DatabaseInserter inserter = new DatabaseInserter();
try {
List<List<Object>> rows = reader.readExcel(excelPath);
inserter.insertData(rows);
System.out.println("成功导入" + rows.size() + "条记录!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
常见问题与解决方案
| 序号 | 问题描述 | 解决方法 |
|---|---|---|
| 1 | Excel日期格式解析失败 | 改用DateUtil.isCellDateFormatted()检测后,通过DateUtil.getJavaDate()转换 |
| 2 | 大数据量导致内存溢出 | 分块读取Excel(使用SXSSFWorkbook流式API),限制单次加载行列数 |
| 3 | 字符编码乱码 | 确保数据库连接URL添加参数useUnicode=true&characterEncoding=UTF-8 |
| 4 | 主键冲突或唯一约束违反 | 插入前先查询是否存在相同记录,或启用数据库的ON DUPLICATE KEY UPDATE机制 |
相关问答FAQs
Q1: 如果Excel中有合并单元格怎么办?
A: 合并单元格在POI中会被视为普通单元格,但其内容仅存储于首个合并区域的锚点位置,可通过sheet.getMergeRegions()获取所有合并区域信息,手动填充缺失的值。
for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
CellRangeAddress region = sheet.getMergedRegion(i);
// 根据业务逻辑处理跨行合并的情况
}
Q2: 如何提高导入速度?
A: 除批处理外,还可采取以下措施:①禁用索引后再导入(适用于MyISAM引擎);②调整数据库事务隔离级别为READ_UNCOMMITTED;③多线程并行处理不同Sheet;④关闭自动提交(conn.setAutoCommit(false)),手动分阶段提交事务
