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

MyBatis 批量插入最佳實踐:foreach 批量寫入、主鍵回填、CDATA 陷阱與 Spring 整合完整示例

     編輯:Java 相關知識

前言

批量插入是提升寫入吞吐的關鍵手段。MyBatis 通過 <foreach> 標籤優雅支持批量 Insert,本文提供完整實戰示例:實體類、Mapper XML、DAO 接口、Spring 整合,並深度解析 foreach 屬性、useGeneratedKeys 主鍵回填、<![CDATA[]]> 導致標籤失效的經典坑點。

實體類

public class TrainRecord implements Serializable {
    private Long id;
    private Long activityId;
    private Long empId;
    private Integer flag;
    private String addTime;
    // Getter/Setter
}

Mapper XML 核心配置

<mapper namespace="com.example.dao.TrainRecordMapper">
    <resultMap id="trainRecordResultMap" type="TrainRecord">
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="add_time" property="addTime" jdbcType="VARCHAR"/>
        <result column="emp_id" property="empId" jdbcType="BIGINT"/>
        <result column="activity_id" property="activityId" jdbcType="BIGINT"/>
        <result column="flag" property="flag" jdbcType="INTEGER"/>
    </resultMap>

    <insert id="addTrainRecordBatch" 
            parameterType="java.util.List" 
            useGeneratedKeys="true" 
            keyProperty="id">  <!-- 主鍵回填關鍵 -->
        <selectKey keyProperty="id" resultType="long" order="AFTER">
            SELECT LAST_INSERT_ID()  <!-- MySQL 獲取自增 ID -->
        </selectKey>
        insert into t_train_record (add_time, emp_id, activity_id, flag)
        values
        <foreach collection="list" item="item" index="index" separator=",">
            (#{item.addTime}, #{item.empId}, #{item.activityId}, #{item.flag})
        </foreach>
    </insert>
</mapper>

foreach 核心屬性詳解

屬性說明示例值
collection要迭代的集合表達式 (必須)list (單參數 List)、array (數組)、map.key (Map 入參)
item迭代元素別名item
index索引別名 (List 為 int,Map 為 key)index
open迭代開始前的字符串(
separator元素間分隔符,
close迭代結束後的字符串)

collection 取值規則:

  • 單參數且為 List → collection="list"
  • 單參數且為數組 → collection="array"
  • 多參數 (或 @Param 封裝 Map) → collection="paramName" (Map 的 key)

主鍵回填機制

  • useGeneratedKeys="true" keyProperty="id":啟用 JDBC getGeneratedKeys(),將數據庫生成的自增 ID 回填到實體 id 字段。
  • <selectKey order="AFTER">SELECT LAST_INSERT_ID()</selectKey>:MySQL 專用補充,確保批量插入後能正確獲取首條記錄 ID (某些驅動版本需要)。
  • Oracle/PostgreSQL 需改用序列 seq.nextval 或 RETURNING id INTO ?。

DAO 接口與 Spring 整合

public interface TrainRecordMapper {
    int addTrainRecordBatch(List<TrainRecord> list);
}

// Spring 掃描 Mapper (需 MapperScannerConfigurer)
@Mapper
public interface TrainRecordMapper { ... }

// Service 調用
@Service
public class TrainRecordService {
    @Autowired TrainRecordMapper mapper;
    public int batchInsert(List<TrainRecord> list) {
        return mapper.addTrainRecordBatch(list);
    }
}

經典坑點:<![CDATA[]]> 導致 foreach 失效

錯誤寫法:

<insert id="addTrainRecordBatch" ...>
    <![CDATA[
        insert into t_train_record (...) values
        <foreach collection="list" item="item" separator=",">
            (#{item.addTime}, ...)
        </foreach>
    ]]>
</insert>

現象: 運行報錯 MySQLSyntaxErrorException,參數全為 null,SQL 中出現字面量 <foreach ...>。

原因: <![CDATA[]]> 內容被視為純文本,**XML 解析器不會解析內部標籤**,<foreach> 失去動態 SQL 能力,變成普通字符串傳給數據庫。

正確寫法: 直接編寫 XML,無需 CDATA。MyBatis XML 本身就是合法 XML,動態標籤 (if, foreach, where 等) 會被解析器識別並處理。只有在 SQL 含有 <、& 等特殊字符且**不在動態標籤內**時,才需在具體屬性值或文本節點使用 CDATA。

性能優化建議

  1. 批次大小控制: 單次批量 500-1000 條,避免 SQL 過長超過數據庫限制 (MySQL max_allowed_packet) 或解析過慢。
  2. 事務邊界: 批量插入必須在同一事務中,否則自動提交導致極慢。
  3. ExecutorType.BATCH: Spring 中 @Transactional + SqlSessionTemplate 設置 executorType=BATCH 可進一步合併 PreparedStatement。
  4. 分區表/分庫: 超大數據量考慮 ShardingSphere 等中間件分片寫入。

總結

MyBatis 批量插入核心三要素:<foreach> 構建 Values 列表、useGeneratedKeys 主鍵回填、拒絕 CDATA 包裹動態標籤。掌握 collection 參數綁定規則、批次大小控制、事務管理,即可構建高性能、可維護的批量寫入功能。

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