EasyExcel与数据库交互的最佳实践

2025-04发布14次浏览

EasyExcel 是阿里巴巴开源的一个用于处理 Excel 文件的 Java 库,其核心优势在于轻量、高效以及对大数据量的支持。在实际项目中,我们经常需要将 EasyExcel 与数据库交互结合使用,比如从数据库读取数据并写入 Excel 文件,或者解析 Excel 文件后将数据插入到数据库中。

下面我们将详细介绍如何实现 EasyExcel 与数据库交互的最佳实践,包括数据读取、写入以及性能优化等关键环节。


1. 环境准备

在开始之前,请确保你的项目已经引入了 EasyExcel 和数据库驱动的相关依赖。例如,Maven 配置如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.0.5</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>

此外,还需要配置好数据库连接池(如 HikariCP 或 Druid)以保证数据库操作的稳定性。


2. 数据库数据写入 Excel 文件

2.1 核心逻辑

从数据库中查询数据,并将其逐行写入 Excel 文件。为了提高效率,可以分批读取数据并使用 EasyExcel 的 write 方法进行批量写入。

2.2 实现步骤

  1. 定义实体类:创建一个与 Excel 表头对应的实体类。

    public class User {
        private String name;
        private Integer age;
        // Getter and Setter
    }
    
  2. 编写数据库查询方法: 使用 JDBC 或 MyBatis 查询数据,并分批加载结果集。

    public List<User> getUsersByBatch(int offset, int limit) {
        String sql = "SELECT name, age FROM user LIMIT ? OFFSET ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, limit);
            pstmt.setInt(2, offset);
            ResultSet rs = pstmt.executeQuery();
            List<User> users = new ArrayList<>();
            while (rs.next()) {
                User user = new User();
                user.setName(rs.getString("name"));
                user.setAge(rs.getInt("age"));
                users.add(user);
            }
            return users;
        } catch (SQLException e) {
            throw new RuntimeException("Database query error", e);
        }
    }
    
  3. 调用 EasyExcel 写入数据: 分批读取数据库数据并写入 Excel 文件。

    public void writeUsersToExcel(String fileName) {
        try (OutputStream out = new FileOutputStream(fileName)) {
            WriteWorkbookHolder workbookHolder = EasyExcel.write(out, User.class).build();
            WriteSheet sheet = EasyExcel.writerSheet("用户数据").build();
            int batchSize = 1000; // 每次读取的数据量
            int offset = 0;
            List<User> users;
            do {
                users = getUsersByBatch(offset, batchSize);
                if (users != null && !users.isEmpty()) {
                    EasyExcel.write(out, User.class)
                            .sheet(sheet.getSheetNo(), sheet.getSheetName())
                            .doWrite(users);
                }
                offset += batchSize;
            } while (!users.isEmpty());
        } catch (IOException e) {
            throw new RuntimeException("Excel write error", e);
        }
    }
    

3. 解析 Excel 文件并将数据写入数据库

3.1 核心逻辑

通过 EasyExcel 解析 Excel 文件,将解析后的数据插入到数据库中。为了避免一次性加载大量数据导致内存溢出,可以采用分批插入的方式。

3.2 实现步骤

  1. 定义监听器:创建一个继承自 AbstractAnalysisEventListener 的监听器类,用于处理每一批解析完成的数据。

    public class UserListener extends AbstractAnalysisEventListener<User> {
        private static final int BATCH_COUNT = 1000;
        private List<User> userList = new ArrayList<>(BATCH_COUNT);
        private DataSource dataSource;
    
        public UserListener(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        @Override
        public void invoke(User data, AnalysisContext context) {
            userList.add(data);
            if (userList.size() >= BATCH_COUNT) {
                saveData();
                userList.clear();
            }
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            if (!userList.isEmpty()) {
                saveData();
            }
        }
    
        private void saveData() {
            String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
            try (Connection conn = dataSource.getConnection();
                 PreparedStatement pstmt = conn.prepareStatement(sql)) {
                for (User user : userList) {
                    pstmt.setString(1, user.getName());
                    pstmt.setInt(2, user.getAge());
                    pstmt.addBatch();
                }
                pstmt.executeBatch();
            } catch (SQLException e) {
                throw new RuntimeException("Database insert error", e);
            }
        }
    }
    
  2. 调用 EasyExcel 解析文件

    public void readExcelAndSaveToDB(String fileName, DataSource dataSource) {
        UserListener listener = new UserListener(dataSource);
        EasyExcel.read(fileName, User.class, listener).sheet().doRead();
    }
    

4. 性能优化建议

  1. 分批处理:无论是写入还是读取,都应尽量避免一次性加载所有数据,分批处理可以显著降低内存消耗。
  2. 数据库索引:对于写入操作,确保目标表的关键字段已建立索引,以加快插入速度。
  3. 多线程支持:如果硬件资源允许,可以通过多线程方式加速数据处理。
  4. 使用缓存:对于频繁查询的数据(如字典表),可以考虑使用缓存机制减少数据库压力。

5. 流程图示例

以下是数据写入和读取的整体流程图:

graph TD
    A[开始] --> B{操作类型}
    B --写入--> C[从数据库分批读取数据]
    C --> D[使用EasyExcel写入Excel]
    D --> E[完成写入]
    B --读取--> F[使用EasyExcel解析Excel]
    F --> G[分批插入数据库]
    G --> H[完成读取]