Tối ưu hóa Xử lý Excel trong Spring Boot với EasyExcel-Plus

Trong quá trình phát triển ứng dụng Spring Boot, việc nhập xuất dữ liệu từ các tệp Excel là một yêu cầu nghiệp vụ phổ biến. Tuy nhiên, các cách triển khai truyền thống thường gặp phải những vấn đề như mã nguồn rườm rà, hiệu suất kém và khó khăn trong việc xử lý các định dạng phức tạp. EasyExcel-Plus, một công cụ nâng cao dựa trên EasyExcel, giải quyết những thách thức này bằng cách cung cấp một giải pháp xử lý Excel hiệu quả, không cần cấu hình phức tạp và dựa trên chú thích (annotation-driven).

Khắc phục các hạn chế của phương pháp xử lý Excel truyền thống

Trước khi EasyExcel-Plus xuất hiện, các nhóm phát triển thường đối mặt với ba vấn đề chính:

  1. Mã nguồn dư thừa: Mỗi giao diện xuất dữ liệu đòi hỏi việc viết lặp lại mã để xử lý luồng tệp, thiết lập kiểu dáng, định nghĩa tiêu đề bảng, v.v.
  2. Chi phí bảo trì cao: Khi có thay đổi nghiệp vụ, cần phải sửa đổi nhiều logic xuất dữ liệu khác nhau, dễ dẫn đến sai sót.
  3. Nút thắt cổ chai về hiệu suất: Việc xuất dữ liệu số lượng lớn thường gây tiêu tốn bộ nhớ và kéo dài thời gian phản hồi.

EasyExcel-Plus khắc phục những điểm yếu này thông qua:

  • Thiết kế hướng chú thích: Sử dụng các chú thích như @ResponseExcel, @RequestExcel cho phép tích hợp mà không cần thay đổi code gốc.
  • Cấu hình thông minh: Tự động nhận diện kiểu dữ liệu và tích hợp các bộ chuyển đổi thông dụng, giảm thiểu công việc cấu hình.
  • Tối ưu hóa hiệu suất: Dựa trên chế độ phân tích cú pháp SAX của EasyExcel, giúp giảm mức sử dụng bộ nhớ hơn 70%.
  • Tính năng toàn diện: Hỗ trợ ánh xạ từ điển, gộp ô, tiêu đề đa cấp và nhiều tính năng nâng cao khác.

Phân tích tính năng cốt lõi: Bốn module tạo nên giải pháp hoàn chỉnh

1. Cơ chế tự động cấu hình – Trải nghiệm tiện lợi "Out-of-the-Box"

Khả năng tự động cấu hình là điểm nổi bật của EasyExcel-Plus. Thông qua lớp EasyExcelPlusAutoConfiguration, các Bean cần thiết sẽ được đăng ký tự động khi ứng dụng khởi động, bao gồm:

// Các bộ xử lý được đăng ký tự động
@Bean
public ExcelWriteHandler excelRecordWriter() {
    return new ExcelWriteHandler();
}

@Bean
public ExcelReturnValueHandler excelResultHandler() {
    return new ExcelReturnValueHandler(excelRecordWriter());
}

Giá trị cốt lõi: Các nhà phát triển không cần viết bất kỳ mã cấu hình nào. Chỉ cần thêm chú thích @ResponseExcel vào phương thức Controller là có thể xuất Excel.

2. Trung tâm chuyển đổi dữ liệu – Xử lý kiểu thông minh

Dự án tích hợp nhiều bộ chuyển đổi dữ liệu để giải quyết các vấn đề chuyển đổi kiểu dữ liệu phổ biến:

  • LocalDateTimeStringConverter: Tự động chuyển đổi kiểu thời gian Java 8 sang định dạng ngày tháng của Excel.
  • ExcelEnumValueConverter: Ánh xạ thông minh giữa giá trị enum và văn bản hiển thị.
  • ExcelDictValueConverter: Chuyển đổi tra cứu từ điển động từ cơ sở dữ liệu.

Minh họa: Chức năng ánh xạ từ điển giúp chuyển đổi mã số thành văn bản dễ đọc.

3. Xử lý định dạng nâng cao – Tạo báo cáo chuyên nghiệp

Đối với các yêu cầu báo cáo phức tạp, EasyExcel-Plus cung cấp khả năng xử lý định dạng mạnh mẽ:

// Ví dụ gộp ô
@ExcelProperty(value = {"Thông tin kho hàng", "Chi tiết sản phẩm", "Mã sản phẩm"})
@ExcelMergeColumn
private String productId;

Với chú thích @ExcelMergeColumn và lớp chiến lược ExcelMergeStrategy, bạn có thể dễ dàng gộp các ô theo nhóm dựa trên một trường cụ thể.

4. Bộ phân tích cú pháp nhập dữ liệu – Đọc dữ liệu hiệu quả

Chức năng nhập dữ liệu cũng rất mạnh mẽ, hỗ trợ các trình lắng nghe tùy chỉnh và xác thực dữ liệu:

@PostMapping("/data/import")
public List<ProductImportDTO> importProductData(
    @RequestExcel(fileName = "inventory_file") List<ProductImportDTO> importedProducts) {
    // Xử lý danh sách sản phẩm đã nhập
    return importedProducts;
}

Bắt đầu nhanh: 5 phút để xuất file Excel đầu tiên

1. Thiết lập môi trường

Thêm dependency sau vào tệp pom.xml của dự án:

<dependency>
    <groupId>com.wxp</groupId>
    <artifactId>easyexcel-plus-spring-boot-starter</artifactId>
    <version>1.3-SNAPSHOT</version>
</dependency>

2. Định nghĩa mô hình dữ liệu

Tạo lớp DTO và thêm các chú thích cần thiết:

@Data
@ExcelIgnoreUnannotated // Bỏ qua các trường không có @ExcelProperty
public class ProductExportDTO {

    @ExcelProperty("Tên sản phẩm")
    private String productName;

    @ExcelProperty("Mã SKU")
    private String productSku;

    @ExcelProperty("Ngày tạo")
    private LocalDateTime creationDate;

    @ExcelProperty(value = "Trạng thái", converter = ExcelEnumValueConverter.class)
    @ExcelEnumValue(ProductStatusEnum.class)
    private Integer productStatus;
}

3. Tạo giao diện xuất dữ liệu

Trong Controller, thêm phương thức xuất dữ liệu:

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping("/export")
    @ResponseExcel(name = "Danh_sach_san_pham", sheetName = "Du_lieu_san_pham")
    public List<ProductExportDTO> exportProducts() {
        // Lấy dữ liệu sản phẩm từ service
        return productService.retrieveAllProducts();
    }
}

4. Kiểm tra kết quả xuất

Truy cập giao diện /api/products/export, trình duyệt sẽ tự động tải xuống tệp Excel. Minh họa: Hiển thị dữ liệu cơ bản với tiêu đề rõ ràng và định dạng chính xác.

Ứng dụng nâng cao: Giải quyết các tình huống nghiệp vụ phức tạp

1. Hai phương pháp ánh xạ từ điển

Phương pháp 1: Ánh xạ Enum (từ điển tĩnh)
@AllArgsConstructor
@Getter
public enum ProductCategoryEnum implements ExcelEnum<Integer> {

    ELECTRONICS(1, "Điện tử"),
    CLOTHING(2, "Thời trang"),
    BOOKS(3, "Sách");

    private Integer code;
    private String description;

    @Override
    public String getByCode(Integer code) {
        return Arrays.stream(values())
            .filter(e -> e.code.equals(code))
            .findFirst()
            .map(ProductCategoryEnum::getDescription)
            .orElse(code.toString());
    }
}
Phương pháp 2: Ánh xạ cơ sở dữ liệu (từ điển động)
@Service
public class ConfigurationService implements DictService {

    @Override
    public Map<String, String> getCodeValueMap(String dictKey) {
        // Truy vấn dữ liệu từ điển từ cơ sở dữ liệu
        List<ConfigEntry> entries = configRepository.findByDictKey(dictKey);
        return entries.stream()
            .collect(Collectors.toMap(ConfigEntry::getValue, ConfigEntry::getLabel));
    }
}

2. Gộp ô và tiêu đề đa cấp

Đối với các định dạng báo cáo phức tạp, bạn có thể sử dụng tiêu đề đa cấp và gộp ô:

@Data
@ExcelIgnoreUnannotated
public class InventoryReportItemDTO {

    @ExcelProperty(value = {"Báo cáo tồn kho", "Thông tin chung", "Mã nhóm"})
    @ExcelMergeColumn // Gộp theo trường này
    private String groupCode;

    @ExcelProperty(value = {"Báo cáo tồn kho", "Thông tin chung", "Tên nhóm"})
    private String groupName;

    @ExcelProperty(value = {"Báo cáo tồn kho", "Chi tiết sản phẩm", "Số lượng tồn"})
    private Integer stockQuantity;
}

Minh họa: Hiệu ứng gộp ô giúp nhóm dữ liệu theo mã nhóm.

3. Trình lắng nghe nhập dữ liệu tùy chỉnh

Đối với các tình huống nhập dữ liệu cần xác thực, bạn có thể tạo trình lắng nghe tùy chỉnh:

public class ProductDataValidator extends DefaultListReadListener<ProductImportDTO> {

    @Override
    public void invoke(ProductImportDTO data, AnalysisContext context) {
        // Logic xác thực dữ liệu
        if (StringUtils.isEmpty(data.getProductCode())) {
            throw new ExcelPlusException("Mã sản phẩm không được để trống");
        }

        // Xử lý logic nghiệp vụ
        processImportedProduct(data);
    }
}

Tối ưu hóa hiệu suất và các phương pháp hay nhất

1. Tối ưu hóa xuất dữ liệu số lượng lớn

Đối với việc xuất hơn 100.000 dòng dữ liệu, khuyến nghị:

  1. Truy vấn theo lô: Sử dụng phân trang để tránh tải toàn bộ dữ liệu vào bộ nhớ cùng một lúc.
  2. Ghi theo luồng: Tận dụng khả năng ghi theo luồng của EasyExcel để giảm mức sử dụng bộ nhớ.
  3. Xử lý không đồng bộ: Đối với dữ liệu cực lớn, hãy xem xét xuất không đồng bộ kết hợp với chức năng kiểm tra tiến độ.

2. Đề xuất tối ưu hóa kiểu dáng

Mặc dù EasyExcel-Plus cung cấp kiểu dáng mặc định, bạn có thể kiểm soát chi tiết hơn bằng các chú thích gốc của EasyExcel:

@ContentStyle(fillPatternType = FillPatternType.SOLID_FOREGROUND,
              fillForegroundColor = 40) // Đặt màu nền
@ColumnWidth(25) // Đặt chiều rộng cột
@HeadFontStyle(fontHeightInPoints = 14, bold = true) // Đặt kiểu chữ tiêu đề
private String keyInformationField;

3. Cơ chế xử lý lỗi

Dự án cung cấp cơ chế xử lý lỗi toàn diện:

  • ExcelPlusException: Lớp ngoại lệ tùy chỉnh để xử lý lỗi trong quá trình xử lý Excel.
  • ErrorMessage: Đóng gói thông báo lỗi để dễ dàng thống nhất định dạng phản hồi lỗi.
  • Xử lý ngoại lệ toàn cục: Kết hợp với @ControllerAdvice của Spring Boot để xử lý ngoại lệ đồng bộ.

Các trường hợp ứng dụng thực tế

1. Xuất báo cáo đơn hàng thương mại điện tử

Một nền tảng thương mại điện tử cần xuất báo cáo đơn hàng hàng ngày với các yêu cầu:

  • Hiển thị đơn hàng theo nhóm cửa hàng.
  • Định dạng số tiền.
  • Ánh xạ từ điển trạng thái đơn hàng.
  • Định dạng thời gian giao hàng.

Giải pháp với EasyExcel-Plus:

@GetMapping("/orders/export")
@ResponseExcel(name = "Bao_cao_don_hang", sheetName = "Du_lieu_don_hang", isMerge = true)
public List<SalesOrderExportDTO> exportDailyOrders(@RequestParam String reportDate) {
    return salesService.getOrdersByDate(reportDate);
}

2. Hệ thống quản lý tài sản

Một hệ thống quản lý tài sản cần xuất thông tin tài sản, bao gồm:

  • Thông tin vị trí gộp hiển thị.
  • Ánh xạ từ điển loại tài sản.
  • Định dạng ngày mua.
  • Bảo vệ thông tin giá trị (ẩn danh).

Mã triển khai:

@Data
@ExcelIgnoreUnannotated
public class AssetDetailsExportDTO {

    @ExcelProperty(value = {"Thông tin tài sản", "Vị trí", "Tên địa điểm"})
    @ExcelMergeColumn
    private String locationName;

    @ExcelProperty(value = {"Thông tin tài sản", "Chi tiết", "Tên tài sản"})
    private String assetName;

    @ExcelProperty(value = {"Thông tin tài sản", "Chi tiết", "Loại"})
    @ExcelEnumValue(AssetTypeEnum.class)
    private Integer assetType;

    @ExcelProperty(value = {"Thông tin tài sản", "Tài chính", "Giá trị mua"})
    @ContentStyle(dataFormat = "$#,##0.00")
    private BigDecimal purchaseValue;
}

Lộ trình học tập và tài nguyên

1. Thứ tự học các module cốt lõi

  1. Xuất cơ bản: Nắm vững cách sử dụng cơ bản của @ResponseExcel và @ExcelProperty.
  2. Ánh xạ từ điển: Học cách triển khai từ điển Enum và từ điển cơ sở dữ liệu.
  3. Xử lý định dạng: Hiểu cách cấu hình gộp ô và tiêu đề đa cấp.
  4. Chức năng nhập: Nắm vững @RequestExcel và các trình lắng nghe tùy chỉnh.
  5. Tính năng nâng cao: Học các kỹ thuật tùy chỉnh kiểu dáng và tối ưu hóa hiệu suất.

2. Khắc phục sự cố thường gặp

Hiện tượng lỗi Nguyên nhân có thể Giải pháp
Tệp xuất bị lỗi mã hóa Vấn đề cài đặt mã hóa Thêm fileNameCharset = "UTF-8" vào @ResponseExcel
Ánh xạ từ điển không hoạt động Enum chưa triển khai interface ExcelEnum Đảm bảo lớp enum triển khai interface ExcelEnum và ghi đè phương thức getByCode
Gộp ô không có hiệu lực Chưa đặt thuộc tính isMerge Đặt isMerge = true trong @ResponseExcel
Dữ liệu nhập trống Định dạng tệp không khớp Kiểm tra xem định dạng tệp Excel và định nghĩa tiêu đề có nhất quán không

3. Đề xuất mở rộng

Đối với các tình huống có yêu cầu đặc biệt, bạn có thể mở rộng các chức năng sau:

  1. Bộ chuyển đổi tùy chỉnh: Kế thừa interface Converter để triển khai chuyển đổi kiểu dữ liệu cụ thể.
  2. Chiến lược kiểu dáng: Triển khai interface CellWriteHandler để tùy chỉnh kiểu dáng ô.
  3. Mẫu xuất: Kết hợp với tệp mẫu để xuất các định dạng phức tạp.
  4. Xác thực dữ liệu: Thêm các quy tắc nghiệp vụ xác thực trong trình lắng nghe.

EasyExcel-Plus, với các chú thích đơn giản và cấu hình thông minh, đã đơn giản hóa đáng kể công việc xử lý Excel trong các dự án Spring Boot. Dù là xuất dữ liệu đơn giản hay tạo báo cáo phức tạp, bạn đều có thể tìm thấy giải pháp phù hợp. Công cụ này đã được kiểm chứng trong môi trường sản xuất, có khả năng xử lý ổn định hàng trăm nghìn dòng dữ liệu mỗi ngày, là lựa chọn xuất sắc cho các nhà phát triển Java khi làm việc với dữ liệu Excel.

Thẻ: Spring Boot Excel easyexcel Data Export Data Import

Đăng vào ngày 9 tháng 11 lúc 16:56