Hướng Dẫn Viết Câu Lệnh SQL Động Trong MyBatis

Tổng Quan Về Dynamic SQL

Trong quy trình phát triển ứng dụng chuẩn hóa sử dụng JDBC thuần túy, việc xây dựng các câu lệnh truy vấn chứa nhiều điều kiện phức tạp thường gặp khó khăn do phải thực hiện thao niệm chuỗi ký tự thủ công. Quy trình này không chỉ tốn thời gian mà còn tiềm ẩn nguy cơ lỗi cú pháp cao. Giải pháp được đưa ra bởi MyBatis cho phép nhà phát triển định nghĩa các đoạn mã SQL linh hoạt thông qua OGNL (Object Graph Navigation Language), giúp loại bỏ gánh nặng concatenation và đảm bảo tính chính xác khi tạo query.

Công cụ hỗ trợ chính cho tính năng này hoạt động tương tự như JSTL trên nền tảng Java Web nhưng tích hợp sâu hơn vào lớp ORM. Phiên bản MyBatis 3 đã tối ưu hóa bộ sưu tập thẻ, giảm đáng kể số lượng component cần học so với các phiên bản trước nhờ sử dụng biểu thức OGNL mạnh mẽ.

Xử Lý Điều Kiện Đơn Giản - Thẻ

Một trường hợp phổ biến nhất là thêm phần tử điều kiện vào mệnh đề WHERE tùy thuộc vào sự tồn tại của giá trị đầu vào. Ví dụ dưới đây minh họa cách lọc dữ liệu blog khi có tham số tiêu đề hoặc không.

<!-- Ví dụ tìm kiếm danh sách Blog -->
<select id="timSanPhamTheotieuDe" resultType="BlogModel">
    SELECT * FROM BLOG_TABLE 
    WHERE trangThai = 'HOAT_DONG'
    
    <if test="ten != null">
        AND tenSanPham LIKE #{ten}
    </if>
</select>

Nếu tham số `ten` bị trả về null, hệ thống sẽ chỉ trả về tất cả các bản ghi có trạng thái `HOAT_DONG`. Ngược lại, nếu có nội dung đầu vào, truy vấn sẽ kết hợp thêm điều kiện khớp tên. Đối với các yêu cầu mở rộng hơn như lọc theo tiêu đề hoặc tác giả cùng lúc:

<select id="timSanPhamKetHop" resultType="BlogModel">
    SELECT * FROM BLOG_TABLE
    WHERE trangThai = 'HOAT_DONG'
    
    <if test="ten != null">
        AND tenSanPham LIKE #{ten}
    </if>
    
    <if test="tacGia.ten != null">
        AND ma_tac_gia LIKE #{tacGia.ten}
    </if>
</select>

Chọn Lựa Duy Nhất - Thẻ , ,

Trường hợp bạn không muốn áp dụng tất cả các điều kiện đồng thời mà cần chọn lựa duy nhất một nhánh xử lý dựa trên tiêu chí ưu tiên, MyBatis cung cấp cấu trúc tương tự câu lệnh switch-case.

Xét ví dụ sau: Ưu tiên tìm kiếm theo tên nếu có, tiếp theo là tác giả nếu không có tên, cuối cùng là danh sách gợi ý (featured) nếu không có thông tin lọc nào.

<select id="timSanPhamTheoUuTien" resultType="BlogModel">
    SELECT * FROM BLOG_TABLE 
    WHERE trangThai = 'HOAT_DONG'
    
    <choose>
        <when test="ten != null">
            AND tenSanPham LIKE #{ten}
        </when>
        
        <when test="tacGia.ten != null">
            AND ma_tac_gia LIKE #{tacGia.ten}
        </when>
        
        <otherwise>
            AND sanPhamNoiBat = 1
        </otherwise>
    </choose>
</select>

Bổ Sung Tự Động Mệnh Đề Where Và Set

Một vấn đề kinh điển khi dùng thẻ `` nằm ở chỗ làm thế nào để xử lý từ khóa bắt đầu `WHERE` hoặc dấu phẩy thừa ở cuối. Nếu không có điều kiện nào thỏa mãn, truy vấn sinh ra có thể bị sai cú pháp (ví dụ: thiếu chỗ đứng cho `WHERE` hoặc thừa chữ `AND`).

MyBatis giải quyết vấn đề này bằng thẻ `` thông minh. Nó tự động chèn từ khóa `WHERE` khi thấy nội dung con khác rỗng, đồng thời loại bỏ các tiền tố `AND` hoặc `OR` ở vị trí đầu tiên.

<select id="timTheoNhieuDieuKien" resultType="BlogModel">
    SELECT * FROM BLOG_TABLE 
    
    <where>
        <if test="trangThai != null">
            trangThai = #{trangThai}
        </if>
        <if test="ten != null">
            AND tenSanPham LIKE #{ten}
        </if>
        <if test="tacGia.ten != null">
            AND ma_tac_gia LIKE #{tacGia.ten}
        </if>
    </where>
</select>

Về nguyên tắc, thẻ này thực chất là wrapper của thẻ `` với thuộc tính prefix là "WHERE". Bạn cũng có thể định nghĩa lại thủ công nếu cần tùy biến sâu hơn:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ...
</trim>

Tương tự, đối với câu lệnh cập nhật dữ liệu, thẻ `` đóng vai trò quan trọng. Nó thêm tiền tố `SET` và tự động loại bỏ dấu phẩy thừa ở cuối mỗi cặp giá trị cập nhật.

<update id="capNhatThongTinTaiKhoan">
    UPDATE USER_INFO
    
    <set>
        <if test="username != null">ten_dang_nhap=#{username},</if>
        <if test="password != null">mat_khau=#{password},</if>
        <if test="email != null">dia_chi_email=#{email},</if>
        <if test="bio != null">tieu_su=#{bio}</if>
    </set>
    
    WHERE ma_user = #{id}
</update>

Kết quả của thẻ set cũng có thể được mô phỏng bằng trim override hậu tố:

<trim prefix="SET" suffixOverrides=", ">
    ...
</trim>

Lặp Duyệt Bộ Sưu Tập - Thẻ

Khi cần kiểm tra xem một giá trị có thuộc tập hợp nào đó không (kiểu điều kiện IN list), thẻ `` cho phép duyệt qua danh sách các đối tượng đầu vào.

<select id="layDanhSachDonHang" resultType="OrderModel">
    SELECT * FROM ORDER_T 
    WHERE ma_don_hang IN 
        
    <foreach item="mavalue" collection="list" 
             open="(" separator="," close=")">
         #{mavalue}
    </foreach>
</select>

Thuộc tính quan trọng bao gồm `collection` (danh sách đầu vào), `item` (biến đại diện từng phần tử), `separator` (ngăn cách giữa các giá trị), và `open/close` (vòng kẹp đầu/cuối). Thẻ này hiểu rõ việc thêm dấu ngăn cách để tránh lỗi cú pháp cuối mảng. Nó hỗ trợ bất kỳ object nào implements Iterable như List, Set, Map hoặc Array.

Sử Dụng Trong Mapper Theo Java Annotation

Khi làm việc trực tiếp với file interface Java thay vì XML mapping, ta có thể nhúng logic động ngay trong注解 Update hoặc Select thông qua thẻ `