MyBatis 作為半自動 ORM 框架,其核心優勢在於 SQL 完全可控與強大的映射能力。本文系統講解五大核心特性:getMapper() 強類型代理、resultMap 複雜結果映射、typeAlias 別名簡化、sql 片段復用、動態 SQL 標籤(where/set/if/choose)。
傳統 session.selectList("namespace.id") 為弱類型(字符串鍵),易拼寫錯誤且無 IDE 支援。getMapper() 通過 JDK 動態代理生成 Mapper 接口實現類,實現強類型調用:
public interface IDeptDao {
List<Dept> getAllDept(); // 方法名 = XML 中 select id
}
<mapper namespace="cn.happy.dao.IDeptDao">
<select id="getAllDept" resultType="cn.happy.entity.Dept">
select * from Dept
</select>
</mapper>
IDeptDao mapper = session.getMapper(IDeptDao.class);
List<Dept> list = mapper.getAllDept(); // 編譯期檢查,IDE 跳轉、重構友好
MyBatis 使用 MapperProxyFactory 創建代理類,攔截方法調用 → 解析 MapperMethod → 執行 SqlSession 底層查詢 → 結果映射返回。接口方法名必須與 XML id 完全一致,參數類型需匹配 parameterType。
當實體類屬性名與表字段名不一致(如駝峰 vs 下劃線),或需關聯查詢(一對一、一對多)時,必須使用 resultMap:
<resultMap type="cn.resultMap.entity.Emp" id="empMap">
<id property="empId" column="EMPID"/>
<result property="empName" column="EMPNAME"/>
<result property="empCity" column="EMPCITY"/>
<!-- 多方實體嵌入一方:使用 association -->
<association property="dept" javaType="cn.resultMap.entity.Dept">
<result property="deptName" column="DEPTNAME"/>
<result property="deptNo" column="DEPTNO"/>
</association>
</resultMap>
<select id="getAllEmps" resultMap="empMap">
select e.*, d.* from Emp e, Dept d where e.deptNo = d.deptNo
</select>
id:標識主鍵列,對比優化(緩存、嵌套結果去重)。association:一對一關聯(多方持有一方引用)。collection:一對多關聯(一方持有多方列表),ofType 指定泛型類型。<sql id="deptColumns"> d.deptNo, d.deptName </sql>
<select id="getAllEmps" resultMap="empMap">
select e.*, <include refid="deptColumns"/>
from Emp e, Dept d where e.deptNo = d.deptNo
</select>
<sql> 定義可復用片段,<include refid="..."/> 引入。支援參數化:<sql id="cols"> ${columns} </sql>。
<typeAliases>
<typeAlias type="cn.resultMap.entity.Emp" alias="emp"/>
<!-- 批量掃描包下所有類,別名默認為類名首字母小寫 -->
<package name="cn.resultMap.entity"/>
</typeAliases>
<select id="getAllEmps" resultMap="empMap" resultType="emp"> ... </select>
內建別名:_byte, _short, _int, _long, _float, _double, _boolean, string, map, list, array 等。
核心標籤:if、choose/when/otherwise、where、set、trim、foreach、bind。
<select id="testAllEmpBySelect" parameterType="emp" resultType="emp">
select * from Emp
<where>
<if test="empId != null"> and empId = #{empId} </if>
<if test="empName != null and empName != ''"> and empName = #{empName} </if>
<if test="empCity != null"> and empCity = #{empCity} </if>
</where>
</select>
<where> 智能處理:若內容為空則不輸出 WHERE;若以 AND/OR 開頭則自動去除首個關鍵字。
<update id="testUpdate" parameterType="dept">
update dept
<set>
<if test="deptNo != null"> deptNo = #{deptNo}, </if>
<if test="deptName != null"> deptName = #{deptName}, </if>
</set>
where deptNo = #{deptNo}
</update>
<set> 智能處理:若內容為空則不輸出 SET;自動去除末尾逗號。
<select id="search" resultType="emp">
select * from Emp
<where>
<choose>
<when test="id != null"> empId = #{id} </when>
<when test="name != null"> empName like #{name} </when>
<otherwise> 1=1 </otherwise>
</choose>
</where>
</select>
// 動態查詢測試
Emp emp = new Emp();
emp.setEmpCity("sh");
List<Emp> list = session.selectList("cn.resultMap.dao.IEmpDao.testAllEmpBySelect", emp);
// 動態更新測試
Dept dept = new Dept();
dept.setDeptNo(1);
dept.setDeptName("財政部");
int rows = session.update("cn.resultMap.dao.IDeptDao.testUpdate", dept);
session.commit();
getMapper():類型安全、重構友好、減少字符串錯誤。resultMap:resultType 僅適用於字段名與屬性名完全一致的簡單場景。<sql>:減少維護成本,保證一致性。typeAliases 掃描包:避免逐個手工註冊。where/set/trim:避免手動拼接 SQL 導致的語法錯誤與注入風險。#{} 預編譯參數 vs ${} 字符串替換:默認用 #{} 防注入;${} 僅用於表名、列名、排序字段等不可預編譯場景。掌握這五大核心特性,即可駕馭 MyBatis 90% 以上的實戰場景:從簡單 CRUD 到複雜多表關聯、動態報表查詢、批量操作。關鍵在於理解「SQL 與對象映射的分離」與「動態構建 SQL 的聲明式邏輯」。