在 MyBatis 中使用?useGeneratedKeys="true"
?獲取新插入記錄的自增 ID 值,可通過以下步驟實現:
1.?配置 Mapper XML
在插入語句的?<insert>
?標簽中設置:
xml
復制
下載
運行
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name, email)VALUES (#{name}, #{email}) </insert>
-
useGeneratedKeys="true"
:啟用數據庫生成的主鍵(如自增 ID)。 -
keyProperty="id"
:將生成的主鍵值賦給參數對象(如?User
?對象)的?id
?屬性。
2.?定義實體類
確保實體類有對應的屬性(與?keyProperty
?一致):
java
復制
下載
public class User {private Long id; // 屬性名必須與 keyProperty 匹配private String name;private String email;// Getter & Setter }
3.?調用 Mapper 方法
插入后,自動填充的 ID 會直接賦給傳入的實體對象:
java
復制
下載
User newUser = new User(); newUser.setName("John"); newUser.setEmail("john@example.com");// 執行插入(返回的是影響行數,非 ID) int rows = userMapper.insertUser(newUser); // 插入后,自增 ID 已注入 newUser 的 id 屬性 Long newId = newUser.getId(); // ? 直接獲取 System.out.println("新記錄的 ID:" + newId);
關鍵點說明
-
keyProperty
?必須匹配實體屬性名
如實體屬性為?userId
,則需配置?keyProperty="userId"
。 -
支持批量插入(MyBatis 3.3.1+)
配置?keyProperty
?為集合元素的屬性:xml
復制
下載
運行
<insert id="insertUsers" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name) VALUES <foreach collection="list" item="user" separator=",">(#{user.name})</foreach> </insert>
調用后,每個對象的?
id
?都會被賦值:java
復制
下載
List<User> users = Arrays.asList(new User("A"), new User("B")); userMapper.insertUsers(users); users.forEach(u -> System.out.println(u.getId())); // 輸出所有新 ID
-
數據庫兼容性
-
MySQL/SQL Server:直接支持?
useGeneratedKeys
。 -
Oracle:需用?
<selectKey>
?配合序列(非本方案范疇)。
-
常見問題
-
獲取值為?
null
?
檢查?keyProperty
?是否與實體屬性名一致,或數據庫是否真的生成了自增 ID。 -
批量插入無效?
確保 MyBatis 版本 ≥ 3.3.1,且?keyProperty
?指向集合元素的屬性(如?id
)。
通過此方案,插入后無需額外查詢,ID 值直接注入對象屬性,高效簡潔。
在 MyBatis 中,keyProperty
?可以指定為參數對象中的任意屬性路徑(包括嵌套對象或 Map 的鍵值),不一定必須是主實體對象的屬性。以下是幾種靈活用法:
1. 指定參數對象的嵌套屬性
若參數是一個包含 ID 容器的復合對象:
java
復制
下載
public class InsertParam {private User user;private Long generatedId; // 專門接收 ID 的屬性// Getter & Setter }
Mapper XML 配置:
xml
復制
下載
運行
<insert id="insertUser" parameterType="InsertParam" useGeneratedKeys="true" keyProperty="generatedId">INSERT INTO user(name) VALUES (#{user.name}) </insert>
插入后獲取:
java
復制
下載
InsertParam param = new InsertParam(); param.setUser(new User("Alice")); userMapper.insertUser(param);Long newId = param.getGeneratedId(); // ? 從專用屬性獲取
2. 使用?Map
?參數接收
直接通過 Map 傳遞參數并接收 ID:
xml
復制
下載
運行
<insert id="insertUser" parameterType="map"useGeneratedKeys="true" keyProperty="resultId">INSERT INTO user(name) VALUES (#{name}) </insert>
Java 調用:
java
復制
下載
Map<String, Object> params = new HashMap<>(); params.put("name", "Bob");userMapper.insertUser(params);Long newId = (Long) params.get("resultId"); // ? 從 Map 獲取
3. 多參數場景(結合?@Param
)
當方法有多個參數時,用?@Param
?指定命名空間:
java
復制
下載
int insertUser(@Param("user") User user,@Param("idHolder") Map<String, Long> idHolder // 專門存 ID 的 Map );
Mapper XML 配置:
xml
復制
下載
運行
<insert id="insertUser" useGeneratedKeys="true" keyProperty="idHolder.id">INSERT INTO user(name) VALUES (#{user.name}) </insert>
插入后獲取:
java
復制
下載
Map<String, Long> idHolder = new HashMap<>(); User user = new User("Charlie");userMapper.insertUser(user, idHolder);Long newId = idHolder.get("id"); // ? 從獨立 Map 獲取
關鍵規則
-
路徑匹配原則
keyProperty
?的值是 OGNL 表達式,需嚴格匹配參數對象的屬性路徑:-
單參數:直接寫屬性名(如?
generatedId
) -
多參數:用?
@Param
?別名前綴(如?idHolder.id
)
-
-
批量插入場景
批量操作時?keyProperty
?需指向集合元素的屬性(不支持外部容器):xml
復制
下載
運行
<!-- 正確:ID 注入到每個元素的 id 屬性 --> <insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">INSERT INTO user(name) VALUES <foreach item="item" collection="list" separator=",">(#{item.name})</foreach> </insert>
-
常見問題
-
屬性未注入??檢查?
keyProperty
?路徑是否存在 setter 方法 -
返回?
null
??確保數據庫自增 ID 已生效(如 MySQL 的?AUTO_INCREMENT
)
-
使用建議
-
專用 ID 容器:推薦用獨立?
Map
?或 DTO 屬性接收 ID,避免污染實體對象 -
明確路徑:多參數時用?
@Param
?定義清晰命名空間(如?keyProperty="holder.id"
) -
事務安全:插入后立即從參數對象中獲取 ID,確保在同一事務內
當 Mapper 方法有多個參數時,MyBatis 無法確定將生成的自增 ID 注入到哪個參數中。以下是詳細解決方案:
錯誤原因分析
復制
下載
Cause: org.apache.ibatis.executor.ExecutorException: Could not determine which parameter to assign generated keys to. Note that when there are multiple parameters, 'keyProperty' must include the parameter name
-
當 Mapper 方法有多個參數時,必須使用?參數名前綴?明確指定注入位置
-
您當前配置的?
keyProperty="generatedId"
?缺少參數名前綴 -
MyBatis 找到的可用參數:
[generatedId, ckDetailId, param1, param2]
解決方案:添加參數名前綴
方法 1:使用?@Param
?注解明確命名參數
java
復制
下載
// Mapper 接口方法 int insertRecord(@Param("data") Map<String, Object> data, // 數據參數@Param("idHolder") Map<String, Long> idHolder // 專門接收ID的參數 );
xml
復制
下載
運行
<!-- Mapper XML 配置 --> <insert id="insertRecord" useGeneratedKeys="true" keyProperty="idHolder.generatedId"> <!-- 添加參數名前綴 -->INSERT INTO your_table(...) VALUES (#{data.field1}, #{data.field2}) </insert>
方法 2:使用 MyBatis 默認參數名(不推薦)
xml
復制
下載
運行
<insert id="insertRecord" useGeneratedKeys="true" keyProperty="param2.generatedId"> <!-- 使用paramN格式 -->... </insert>
參數索引從1開始:
param1
=第一個參數,?param2
=第二個參數
完整示例代碼
1. Mapper 接口
java
復制
下載
public interface YourMapper {int insertRecord(@Param("detail") YourEntity entity, // 實體參數@Param("idHolder") Map<String, Long> holder // ID容器參數); }
2. Mapper XML
xml
復制
下載
運行
<insert id="insertRecord" useGeneratedKeys="true"keyProperty="idHolder.generatedId"> <!-- 關鍵:帶前綴的keyProperty -->INSERT INTO your_table(name, ck_detail_id)VALUES (#{detail.name}, #{detail.ckDetailId}) </insert>
3. 調用代碼
java
復制
下載
// 準備參數 YourEntity entity = new YourEntity("test", 1001); Map<String, Long> idHolder = new HashMap<>(); // 專門接收ID// 執行插入 mapper.insertRecord(entity, idHolder); // 獲取自增ID (注意鍵名與XML中一致) Long newId = idHolder.get("generatedId"); System.out.println("Generated ID: " + newId);
關鍵注意事項
-
前綴必須匹配?
@Param
?值
keyProperty="idHolder.generatedId"
?對應:-
idHolder
?→?@Param("idHolder")
-
generatedId
?→ Map中的鍵名
-
-
Map 接收 vs 對象屬性
xml
復制
下載
運行
<!-- 如果使用對象屬性接收 --> keyProperty="detail.id" <!-- 注入到實體對象的id屬性 --><!-- 如果使用Map接收 --> keyProperty="idHolder.generatedId" <!-- 注入到Map的generatedId鍵 -->
-
批量插入特殊處理
批量操作時仍需注入到集合元素內:xml
復制
下載
運行
<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">INSERT ... <!-- 無法注入到外部參數 --> </insert>
常見錯誤排查
錯誤現象 | 解決方案 |
---|---|
返回null | 檢查數據庫自增列是否生效 |
報Invalid bound statement | 檢查keyProperty 前綴是否匹配@Param 值 |
批量插入ID未注入 | 確保keyProperty 指向集合元素的屬性 |
Oracle數據庫不生效 | 需使用<selectKey> 替代 |
建議:對多參數場景始終使用?
@Param
?+ 前綴的?keyProperty
,避免依賴?paramN
?隱式命名