首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 直接執行動態 SQL 與批量插入最佳實踐:${} 語法、ResultMap 複用與 Foreach 批處理

     編輯:Java 相關知識

前言:突破映射限制的動態 SQL 與高效批處理

MyBatis 標準模式要求預定義 SQL 與映射,但實際場景中常需動態構建查詢(如報表引擎、動態表單)或高性能批量寫入。本文深度解析 ${} 直接注入 SQL、ResultMap 靈活映射、<foreach> 批量插入三大核心技術,並給出生產級最佳實踐。

一、直接執行動態 SQL:${} 語法詳解與安全邊界

1. #{} vs ${} 本質區別

特性#{} (預編譯參數)${} (字符串直接替換)
機制PreparedStatement 參數綁定 (?) SQL 字符串拼接,直接替換
類型處理自動類型轉換、加引號、防注入無任何處理,原樣輸出
適用場景WHERE 條件值、INSERT/UPDATE 值表名、列名、ORDER BY 欄位、完整 SQL 片段
安全性安全,防 SQL 注入極高風險,必須嚴格校驗輸入

2. 動態查詢實戰:傳入完整 SQL 片段

<!-- Mapper.xml -->
<select id="queryByDynamicSql" resultType="com.example.model.InstanceModel">
    ${paramSql}
</select>

// Mapper Interface
List<InstanceModel> queryByDynamicSql(@Param("paramSql") String sql);

調用規範:傳入 SQL 必須包含別名匹配 ResultMap/resultType 屬性:

SELECT inst_id AS instanceId, inst_name AS instanceName FROM instance_table WHERE status = 1

安全防護:

  • 僅允許受信任內部代碼調用,絕不暴露給前端/用戶輸入。
  • 建立 SQL 白名單校驗器,解析 AST 驗證只包含 SELECT、允許的表/列。
  • 使用 PreparedStatement 參數化處理 WHERE 值,僅用 ${} 拼接結構性片段。

3. resultType vs resultMap 選擇

  • resultType:簡單查詢,列名與屬性名一致(或駝峰映射開啟 mapUnderscoreToCamelCase=true)。
  • resultMap:複雜映射(列名不一致、嵌套對象、多結果集、構造器注入),定義一次,多處複用。

二、批量插入高性能實戰:Foreach 批處理模式

1. 核心原理:單條 INSERT 多 VALUES

對比兩種寫法:

-- 低效:多條 INSERT(多次網路往返、多次解析優化)
INSERT INTO student (name, age) VALUES ('A', 18);
INSERT INTO student (name, age) VALUES ('B', 19);

-- 高效:單條 INSERT 多 VALUES(一次往返、批量優化)
INSERT INTO student (name, age) VALUES ('A', 18), ('B', 19), ('C', 20);

2. MyBatis <foreach> 實現

<insert id="batchInsertStudents" parameterType="java.util.List">
    INSERT INTO student (name, sex, address, telephone, tid) VALUES
    <foreach collection="list" item="item" index="index" separator=",">
        (#{item.name}, #{item.sex}, #{item.address}, #{item.telephone}, #{item.tId})
    </foreach>
</insert>

// Mapper Interface
void batchInsertStudents(@Param("list") List<Student> students);

關鍵屬性:

  • collection:集合參數名(單參數 List 為 list,數組為 array,Map 為 key 名)。
  • item:迭代變量別名。
  • separator:元素分隔符(VALUES 間用逗號)。
  • open/close:整體包裹符(如 ( 和 ))。

3. 進階批量插入:主鍵回填、時間戳、分批提交

<insert id="batchInsertOrders" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
    <selectKey keyProperty="createTime" order="BEFORE" resultType="java.lang.String">
        SELECT NOW()  -- 或 CURRENT_TIMESTAMP,統一批次時間
    </selectKey>
    INSERT INTO orders (user_id, amount, create_time, status) VALUES
    <foreach collection="list" item="order" separator=",">
        (#{order.userId}, #{order.amount}, #{createTime}, #{order.status})
    </foreach>
</insert>

大數據量分批策略:

  • MySQL max_allowed_packet 限制單條 SQL 大小(預設 4MB/64MB)。
  • 建議每批 500-1000 條,Service 層分批調用:
public void batchInsertLarge(List<Order> orders) {
    List<List<Order>> batches = Lists.partition(orders, 500);
    for (List<Order> batch : batches) {
        orderMapper.batchInsertOrders(batch);
    }
}

4. 主鍵生成策略對比

策略配置適用場景注意事項
數據庫自增useGeneratedKeys="true" keyProperty="id"MySQL、SQL Server、PostgreSQL (serial)批量插入自動回填 ID,JDBC 驅動需支援
序列/Sequence<selectKey order="BEFORE">SELECT NEXT VALUE FOR seq</selectKey>Oracle、PostgreSQL、DB2需數據庫支援序列,批量需預取 ID 段
UUID/雪花算法Java 端生成,keyProperty 映射分布式、無中心化無需數據庫協調,索引性能略遜自增
無主鍵/業務鍵不配置 useGeneratedKeys日誌表、關係表INSERT 不指定主鍵列

三、綜合最佳實踐清單

  1. 動態 SQL 安全第一:${} 僅用於結構性片段(表/列/排序),值參數堅持 #{}。
  2. 批量寫入用 Foreach 多 VALUES:避免循環調用單條 INSERT,單批 500-1000 條,超限分批。
  3. ResultMap 定義在 Mapper.xml 顶層:跨語句複用,減少重複映射定義。
  4. 批量插入開啟 useGeneratedKeys:自動回填自增 ID,避免二次查詢。
  5. 統一時間戳用 selectKey:批次數據共用一個創建時間,保證一致性。
  6. 事務邊界控制:大批量寫入考慮分批提交,避免長事務鎖擴大、Undo Log 膨脹。
  7. 參數傳遞規範:單參數 List 直接用 collection="list";多參數用 @Param 或 Map 封裝,collection 指向 key。

四、常見坑點排查

現象原因解決
批量插入主鍵不回填JDBC 驅動版本過低 / 未配置 useGeneratedKeys / 數據庫不支援升級驅動、檢查配置、改用 selectKey 預取 ID
動態 SQL 報語法錯誤${} 注入值含特殊字符 / 缺少別名嚴格校驗輸入、SQL 模板化、強制別名
Foreach 參數找不到collection 值與實際參數名不匹配單參數 List 用 list,數組用 array,多參數用 @Param key
批量插入超時/內存溢出單批次過大、SQL 過長、事務未提交分批、增加 max_allowed_packet、調大 fetchSize、分批提交

總結

MyBatis 的 ${} 賦予了動態 SQL 構建能力,<foreach> 實現了高性能批量寫入。核心原則:動態結構用 ${},業務數值用 #{};批量寫入單條多 VALUES,分批控制大小,主鍵回填自動化。嚴守安全邊界與性能邊界,即可在靈活性與穩健性之間游刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved