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
MetaObjectcủ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.DEFAULT | Không xử lý |
FieldFill.INSERT | Chỉ khi chèn |
FieldFill.UPDATE | Chỉ khi cập nhật |
FieldFill.INSERT_UPDATE | Cả 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 settergetGetterType(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.