MyBatis Nguyên Lý Hoạt Động Và Cách Sử Dụng Hiệu Quả

Hiểu Rõ Nguyên Lý Làm Việc Của MyBatis

MyBatis là một framework hỗ trợ mạnh mẽ cho việc thao tác với cơ sở dữ liệu trong các ứng dụng Java. Thay vì viết JDBC thuần, MyBatis giúp giảm thiểu lượng code lặp lại và tăng tính dễ bảo trì bằng cách ánh xạ câu lệnh SQL với các phương thức trong interface.

Cốt Lõi Của MyBatis

  • Mapper Interface: Tất cả các phương thức thao tác CSDL đều được khai báo ở dạng abstract method trong interface (gọi là Mapper).
  • SQL Mapping: Mỗi phương thức sẽ tương ứng với một câu lệnh SQL được định nghĩa trong file XML hoặc qua annotation.
  • Tự động xử lý chi tiết: MyBatis tự quản lý kết nối, transaction, đóng tài nguyên và đặc biệt là ánh xạ kết quả truy vấn sang đối tượng Java.

Khai Báo Phương Thức Trong Mapper

Khi thiết kế phương thức trong Mapper, cần tuân thủ một số quy tắc:

  • Trả về:
    • Thao tác INSERT/UPDATE/DELETE: Dùng Integer để nhận số dòng bị ảnh hưởng (hoặc void nếu không cần).
    • Thao tác SELECT: Dùng kiểu dữ liệu mong muốn. Nếu lấy nhiều bản ghi, dùng List<T>, còn lại dùng trực tiếp class POJO.
  • Tên phương thức: Tự đặt, nhưng không được trùng tên (không cho phép overloading).
  • Tham số: Thiết kế theo nhu cầu. Nếu có từ 2 tham số trở lên, bắt buộc phải dùng @Param để đặt tên rõ ràng.

Ví dụ Khai Báo Phương Thức

public interface UserMapper {
    // Đếm số người dùng
    Integer countUsers();

    // Tìm người dùng theo ID
    User findById(Integer id);

    // Cập nhật email theo ID
    Integer updateEmailById(@Param("id") Integer id, @Param("email") String email);
}

Cấu Hình File XML Mapping

Mỗi Mapper interface cần có một file XML tương ứng để định nghĩa SQL. Cấu trúc cơ bản:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.UserMapper">
    <select id="countUsers" resultType="java.lang.Integer">
        SELECT COUNT(*) FROM t_user
    </select>

    <select id="findById" resultType="com.example.User">
        SELECT * FROM t_user WHERE id = #{id}
    </select>
</mapper>
  • namespace: Phải là tên đầy đủ (fully qualified name) của interface Mapper.
  • id: Tên phương thức tương ứng.
  • resultType: Kiểu trả về. Với List, chỉ cần khai báo kiểu phần tử.

Xử Lý Nhiều Tham Số – Dùng @Param

Khi có nhiều hơn một tham số, Java không lưu tên biến sau khi biên dịch (chỉ còn arg0, arg1...), nên MyBatis không thể ánh xạ đúng. Giải pháp: dùng @Param.

Integer updateUser(
    @Param("id") Integer id,
    @Param("name") String name,
    @Param("email") String email
);

Trong file XML:

<update id="updateUser">
    UPDATE t_user 
    SET username = #{name}, email = #{email} 
    WHERE id = #{id}
</update>

Sử Dụng Dynamic SQL

1. <foreach> – Xử Lý Danh Sách

Dùng để tạo câu lệnh IN() với danh sách giá trị động.

<delete id="deleteByIds">
    DELETE FROM t_user 
    WHERE id IN 
    <foreach collection="list" item="uid" open="(" separator="," close=")">
        #{uid}
    </foreach>
</delete>
  • collection: Với tham số đơn, dùng list (nếu là List), array (nếu là mảng).
  • item: Tên tạm dùng trong vòng lặp.
  • separator: Ký tự phân cách giữa các phần tử.
  • open/close: Mở và đóng ngoặc.

2. <if> Và <choose> – Điều Kiện

<select id="findUsersByCondition" resultType="User">
    SELECT * FROM t_user WHERE 1=1
    <if test="name != null and name != ''">
        AND username LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="age != null">
        AND age >= #{age}
    </if>
</select>

Nếu cần if-else, dùng <choose>:

<choose>
    <when test="type == 'admin'">
        AND role = 'ADMIN'
    </when>
    <otherwise>
        AND status = 'ACTIVE'
    </otherwise>
</choose>

#{} vs ${} – Khác Biệt Về Bảo Mật

  • #{}: PreparedStatement. An toàn, chống SQL Injection. Chỉ dùng cho giá trị.
  • ${}: String interpolation. Dễ bị tấn công SQL Injection. Chỉ dùng khi cần chèn đoạn SQL (ví dụ: tên bảng, cột, ORDER BY).

Khuyến cáo: Luôn ưu tiên dùng #{}, chỉ dùng ${} khi thực sự cần.

Xử Lý Sai Lệch Tên Cột Và Thuộc Tính

Khi tên cột DB khác tên thuộc tính Java (ví dụ: department_iddepartmentId), có hai cách giải quyết:

Cách 1: Dùng Biệt Danh (Alias)

<select id="findAll" resultType="User">
    SELECT id, department_id AS departmentId, username 
    FROM t_user
</select>

Cách 2: Dùng <resultMap>

<resultMap id="UserResultMap" type="User">
    <id property="id" column="id"/>
    <result property="departmentId" column="department_id"/>
    <result property="username" column="username"/>
</resultMap>

<select id="findAll" resultMap="UserResultMap">
    SELECT * FROM t_user
</select>
  • Dùng <id> cho khóa chính để tối ưu cache.
  • Chỉ cần khai báo những trường sai lệch.

Truy Vấn Liên Kết Bảng

1. Một Chiều – Dùng VO Class

Khi truy vấn nhiều bảng, cần tạo lớp VO (Value Object) để chứa kết quả.

public class UserVO {
    private Integer id;
    private String username;
    private String departmentName;
    // getter/setter...
}

Mapper:

UserVO findUserWithDept(@Param("id") Integer id);

SQL:

<select id="findUserWithDept" resultType="UserVO">
    SELECT u.id, u.username, d.name AS departmentName
    FROM t_user u
    LEFT JOIN t_department d ON u.department_id = d.id
    WHERE u.id = #{id}
</select>

2. Một-Nhiều – Dùng <collection>

Ví dụ: Lấy thông tin phòng ban kèm danh sách nhân viên.

public class DepartmentVO {
    private Integer id;
    private String name;
    private List<User> users;
    // getter/setter...
}

Dùng <resultMap> với <collection>:

<resultMap id="DeptWithUsersMap" type="DepartmentVO">
    <id property="id" column="dept_id"/>
    <result property="name" column="dept_name"/>
    <collection property="users" ofType="User">
        <id property="id" column="user_id"/>
        <result property="username" column="user_name"/>
    </collection>
</resultMap>

<select id="findDeptWithUsers" resultMap="DeptWithUsersMap">
    SELECT 
        d.id AS dept_id, d.name AS dept_name,
        u.id AS user_id, u.username AS user_name
    FROM t_department d
    LEFT JOIN t_user u ON d.id = u.department_id
    WHERE d.id = #{id}
</select>

Lỗi Thường Gặp Và Cách Xử Lý

  • Invalid bound statement (not found): Kiểm tra:
    • Đường dẫn file XML có đúng trong cấu hình mapperLocations?
    • namespace trong XML có khớp với interface?
    • id của node có trùng tên phương thức?
  • Không ánh xạ được dữ liệu: Kiểm tra tên cột, alias, hoặc dùng resultMap.

Thẻ: mybatis Java orm sql Maven

Đăng vào ngày 21 tháng 6 lúc 06:25