批量插入是提升寫入吞吐的關鍵手段。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 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>
| 屬性 | 說明 | 示例值 |
|---|---|---|
| collection | 要迭代的集合表達式 (必須) | list (單參數 List)、array (數組)、map.key (Map 入參) |
| item | 迭代元素別名 | item |
| index | 索引別名 (List 為 int,Map 為 key) | index |
| open | 迭代開始前的字符串 | ( |
| separator | 元素間分隔符 | , |
| close | 迭代結束後的字符串 | ) |
collection 取值規則:
collection="list"collection="array"collection="paramName" (Map 的 key)useGeneratedKeys="true" keyProperty="id":啟用 JDBC getGeneratedKeys(),將數據庫生成的自增 ID 回填到實體 id 字段。<selectKey order="AFTER">SELECT LAST_INSERT_ID()</selectKey>:MySQL 專用補充,確保批量插入後能正確獲取首條記錄 ID (某些驅動版本需要)。seq.nextval 或 RETURNING id INTO ?。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);
}
}
錯誤寫法:
<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。
max_allowed_packet) 或解析過慢。@Transactional + SqlSessionTemplate 設置 executorType=BATCH 可進一步合併 PreparedStatement。MyBatis 批量插入核心三要素:<foreach> 構建 Values 列表、useGeneratedKeys 主鍵回填、拒絕 CDATA 包裹動態標籤。掌握 collection 參數綁定規則、批次大小控制、事務管理,即可構建高性能、可維護的批量寫入功能。