Truy vấn động trong MyBatis
Một trong những lợi ích quan trọng của MyBatis là khả năng viết truy vấn SQL động, giúp giảm thiểu việc xử lý điều kiện trong code Java. Khái niệm này không chỉ đơn thuần là truyền tham số mà còn bao gồm các thẻ điều khiển luồng như if, choose, when, v.v. Bạn có thể tham khảo thêm tài liệu chính thức của MyBatis.
Phân biệt #{}, ${}
Trong MyBatis, có hai cách truyền tham số chính: #{} và ${}.
#{}: Sử dụng PreparedStatement, cho phép SQL được biên dịch trước và chỉ thay đổi giá trị tham số. Cấu trúc SQL không bị thay đổi.${}: Thay thế trực tiếp chuỗi ký tự vào câu SQL trước khi biên dịch. Điều này có thể thay đổi cấu trúc SQL.
Ví dụ minh họa:
-- Sử dụng #{}
select * from student where name = #{name};
-- Sử dụng ${}
select * from student where name = ${name};
Giả sử giá trị name là 1 or 1 =1, kết quả sẽ như sau:
-- Với #{}
select * from student where name = '1 or 1 =1';
-- Với ${}
select * from student where name = 1 or 1 =1;
Rõ ràng ${} có thể dẫn đến lỗ hổng SQL injection do thay đổi cấu trúc câu lệnh. Vì vậy, ưu tiên sử dụng #{} là an toàn hơn. Tuy nhiên, không phải lúc nào ${} cũng bị cấm. Hãy xét một số trường hợp đặc biệt:
-- Sai cú pháp khi dùng #{}
select * from student where name like '%#{name}%';
-- Đúng với ${}
select * from student where name like '%${name}%';
Câu lệnh đầu tiên gây lỗi vì PreparedStatement sẽ biến nó thành like '%'name'%', không hợp lệ. Để dùng #{} trong LIKE, có thể kết hợp chuỗi như sau:
select * from student where name like "%"#{name}"%";
Một trường hợp bắt buộc dùng ${} là khi tên bảng hoặc tên cột động:
select * from student_class_${id} where name = 'Tiểu Vương Tử';
Ở đây, tên bảng phải được tạo thành từ biến id, nên không thể dùng #{}.
Sử dụng @Param annotation
Annotation @Param trong MyBatis giúp đóng gói các tham số thành một Map. Nếu bạn đã sử dụng Map làm tham số đầu vào cho mapper, không cần thêm @Param. Tuy nhiên, với các tham số đơn lẻ hoặc nhiều tham số, nên dùng @Param để tránh lỗi không tìm thấy tham số. Lỗi thường gặp khi thiếu @Param:
The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select * from news where news_abstract like %?% and item1 = ?
### Cause: java.sql.SQLSyntaxErrorException: ...
MyBatis-Plus – Tự động hóa hơn
MyBatis-Plus là một extension của MyBatis, giúp giảm thiểu việc viết SQL thủ công. Thay vì viết câu lệnh SQL dài dòng, bạn có thể dùng các API như QueryWrapper:
// MyBatis gốc
select * from student where name = "AA";
// MyBatis-Plus
queryWrapper.eq("name", "AA");
MyBatis-Plus cũng hỗ trợ AutoGenerator để sinh tự động các lớp Controller, Service, Mapper, v.v.
Lưu ý: Sử dụng queryWrapper.groupBy("CODE") có thể gây giảm hiệu năng nghiêm trọng, một truy vấn có thể mất tới 13.2 giây. Vì vậy, cần cân nhắc khi dùng.
Về phân loại tham số trong MyBatis, có ba dạng chính: kiểu đơn (int, String), JavaBean, và các kiểu tập hợp (List, Map). Không nên trộn lẫn kiểu đơn và JavaBean trong cùng một mapper method. Nếu có nhu cầu, hãy đóng gói chúng vào Map.
Truy vấn phân trang với LIMIT
Với MyBatis, nếu muốn tính toán trong LIMIT, bạn phải dùng ${} vì #{} không hỗ trợ phép tính:
SELECT * FROM tb_qualification WHERE id IN
(SELECT A.qualification_id FROM
(SELECT qualification_id FROM tb_trade_qualification WHERE trade_id=#{arg0} LIMIT ${5*arg1},5) AS A);
Tham khảo thêm: MyBatis foreach, MyBatis-Plus Code Generator, Logical Delete.