Tự động điền trường thực thể trong MyBatis-Plus với MetaObjectHandler và MetaObject

MetaObjectHandler là giao diện trong MyBatis-Plus cho phép tự động điền giá trị vào các trường chung của thực thể (như thời gian tạo, người cập nhật...) khi thực hiện thao tác INSERT hoặc UPDATE. Việc này giúp loại bỏ mã lặp và tăng hiệu suất phát triển.

Nguyên lý hoạt động

  • Cơ chế chặn: MyBatis-Plus can thiệp trước khi thực thi SQL để xử lý đối tượng thực thể.
  • Phản xạ thông minh: Sử dụng lớp MetaObject của MyBatis để thiết lập thuộc tính qua phản xạ mà không cần gọi trực tiếp setter/getter.

Cách sử dụng

1. Triển khai MetaObjectHandler

@Component
public class AutoFillHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject meta) {
        strictInsertFill(meta, "createdAt", LocalDateTime.class, LocalDateTime.now());
        strictInsertFill(meta, "createdBy", String.class, getCurrentUser());
    }

    @Override
    public void updateFill(MetaObject meta) {
        strictUpdateFill(meta, "updatedAt", LocalDateTime.class, LocalDateTime.now());
        strictUpdateFill(meta, "updatedBy", String.class, getCurrentUser());
    }

    private String getCurrentUser() {
        // Lấy từ ThreadLocal, SecurityContext, v.v.
        return "system";
    }
}

2. Đánh dấu trường trong thực thể

public class Product {
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;

    @TableField(fill = FieldFill.INSERT)
    private String createdBy;

    @TableField(fill = FieldFill.UPDATE)
    private String updatedBy;
}

3. Các chiến lược điền

Giá trị Mô tả
FieldFill.DEFAULTKhông xử lý
FieldFill.INSERTChỉ khi chèn
FieldFill.UPDATEChỉ khi cập nhật
FieldFill.INSERT_UPDATECả chèn và cập nhật

Phương thức quan trọng

  • strictInsertFill() / strictUpdateFill(): Chỉ gán nếu trường chưa có giá trị.
  • meta.setValue("field", value): Gán giá trị trực tiếp, bất kể trạng thái hiện tại.

Lưu ý quan trọng

  • Chỉ hoạt động với các phương thức CRUD do MyBatis-Plus cung cấp (save(), updateById(),...).
  • Không áp dụng khi dùng Wrapper để cập nhật một phần trường.
  • Nếu lấy giá trị từ ngữ cảnh luồng (ThreadLocal), đảm bảo an toàn luồng.

MetaObject và SystemMetaObject trong MyBatis

MetaObject là công cụ phản xạ mạnh mẽ của MyBatis, cho phép đọc/ghi thuộc tính — kể cả lồng nhau, danh sách, map — mà không cần viết mã phản xạ thủ công.

Khởi tạo MetaObject

MetaObject meta = SystemMetaObject.forObject(anyObject);

Thao tác cơ bản

// Thuộc tính thường
meta.setValue("id", 100L);
Long id = (Long) meta.getValue("id");

// Thuộc tính lồng
meta.setValue("address.city", "Hà Nội");
String city = (String) meta.getValue("address.city");

// Danh sách
meta.setValue("items[0].name", "Laptop");
String name = (String) meta.getValue("items[0].name");

// Map
Map<String, Object> data = new HashMap<>();
MetaObject m = SystemMetaObject.forObject(data);
m.setValue("status", "active");
String status = (String) m.getValue("status");

Phương thức hữu ích

  • getValue(name): Lấy giá trị (hỗ trợ a.b[0].c)
  • setValue(name, value): Gán giá trị
  • hasSetter(name): Kiểm tra tồn tại setter
  • getGetterType(name): Lấy kiểu dữ liệu

Ứng dụng thực tế

  • Tự động điền trường trong interceptor (tạo lúc, cập nhật lúc).
  • Phân tích tham số phân trang trong plugin phân trang.
  • Gán ID tenant trong hệ thống đa thuê.
  • Sao chép thuộc tính giữa các đối tượng hiệu quả hơn BeanUtils.

Yêu cầu và giới hạn

  • Thực thể phải tuân thủ JavaBean: constructor không tham số, getter/setter chuẩn.
  • Tên thuộc tính phân biệt chữ hoa/thường.
  • Không hỗ trợ trường private không có setter.
  • Đối tượng lồng được khởi tạo tự động khi cần.

Thẻ: MyBatis-Plus MetaObject AutoFill Reflection Java

Đăng vào ngày 8 tháng 6 lúc 21:33