MyBatis (前身 iBatis) 是優秀的持久層框架,支援自定義 SQL、存儲過程與高級映射。本文總結實戰中積累的核心優化建議,助你避開常見坑點,編寫高效可維護的資料存取層。
問題:Mapper 介面不支援方法重載,若直接使用多參數會導致參數含義模糊。
建議:Mapper 參數統一使用 Map,在 Service 層提供重載方法,對外暴露清晰的方法簽名。
// Mapper 介面
User selectByParams(Map<String, Object> params);
// Service 層重載
public User getById(Long id) { return mapper.selectByParams(Map.of("id", id)); }
public User getByName(String name) { return mapper.selectByParams(Map.of("name", name)); }
public User getByIdAndName(Long id, String name) {
return mapper.selectByParams(Map.of("id", id, "name", name));
}
優勢:方法簽名自文檔化,參數類型/個數/含義一目瞭然,重構時編譯器可檢查,避免 Map 鍵名拼寫錯誤。
問題:大量動態標籤導致 SQL 複雜難讀、調試困難、產生大量空白字元、引發 Oracle 硬解析。
建議:優先使用 SQL 原生函數處理條件邏輯。
<select id="selectNews" resultType="News">
SELECT * FROM T_NEWS_TEXT WHERE 1=1
<choose>
<if test="startdate != null and enddate != null">
AND PUBLISHTIME >= #{startdate} AND PUBLISHTIME <= #{enddate}
</if>
<otherwise>
AND PUBLISHTIME >= SYSDATE - 7 AND PUBLISHTIME <= SYSDATE
</otherwise>
</choose>
</select>
<select id="selectNews" resultType="News">
SELECT * FROM T_NEWS_TEXT
WHERE PUBLISHTIME >= DECODE(#{startdate}, NULL, SYSDATE-7, #{startdate})
AND PUBLISHTIME <= DECODE(#{enddate}, NULL, SYSDATE, #{enddate})
</select>
補充:預設值處理應前移至 Service/Controller 層,便於快取鍵值確定。
問題:SQL 單行註釋 (--) 在分頁插件包裝 SQL 時會導致後續條件被註釋掉,極難排查。
建議:統一使用 XML 註釋 <!-- -->,不會出現在生成的 SQL 中。
<!-- 正確:XML 註釋 -->
<select id="query">
SELECT * FROM TEST WHERE COL1 > 1
<if test="a != null">AND COL2 = #{a}</if>
</select>
-- 錯誤:SQL 註釋會吞噬後續動態標籤
SELECT * FROM TEST WHERE COL1 > 1 -- 這是註釋
<if test="a != null">AND COL2 = #{a}</if>
問題:${} 為字串直接替換,導致 SQL 注入風險、Oracle 硬解析、效能下降。
建議:
#{} (PreparedStatement)IN 查詢:使用 <foreach> 或自定義 TypeHandlerLIKE 查詢:title LIKE CONCAT('%', #{keyword}, '%') 或 title LIKE #{keyword} || '%'MyBatis 定位為 SQL 映射與簡單 ORM,避免在映射文件中實現複雜業務邏輯、字串處理、流程控制。複雜邏輯應在 Service 層用 Java 完成。
resultType 隱式映射導致欄位名變更時運行時報錯ExecutorType.BATCH 或 <foreach> 批次 INSERTlogImpl=STDOUT_LOGGING 監控執行 SQL 與參數MyBatis 核心優勢在於 SQL 完全可控,但也要求開發者具備良好的 SQL 功力與資料庫知識。遵循「Service 層重載、SQL 原生邏輯、XML 註釋、#{} 參數化、職責單一」五大原則,能顯著提升代碼可維護性與系統效能。