mybatis中usegeneratedkeys未生效,主因是配置位置错误(须写在标签内)、keyproperty与java属性名不匹配、数据库主键未设自增、jdbc参数如rewritebatchedstatements干扰等。

insert 语句没拿到自增 ID,useGeneratedKeys 没生效?
多数情况是配置位置或参数不匹配。MyBatis 的自增主键回填依赖 JDBC 驱动对 getGeneratedKeys() 的支持,而 useGeneratedKeys="true" 必须写在 <insert></insert> 标签上,不是写在 <mapper></mapper> 或全局配置里。
常见错误现象:user.getId() 仍是 0 或 null,数据库明明插入成功了;日志里看到 SQL 执行了,但没打印 ID 回填过程。
-
useGeneratedKeys只对 insert 有效,update/delete 不识别 - 必须同时指定
keyProperty(Java 对象的属性名),且该属性要有 public setter - MySQL 要求表主键是
AUTO_INCREMENT,PostgreSQL 需用SERIAL或IDENTITY列 - HikariCP 等连接池默认允许
getGeneratedKeys(),但某些老版本 Druid 需显式设allowMultiQueries=true
keyProperty 值写错:大小写、嵌套、复数都可能失败
它填的是 Java Bean 的属性名,不是数据库字段名,也不是 getter 方法名。比如实体类是 private Long userId;,那就要写 keyProperty="userId",而不是 "user_id" 或 "getUserId"。
容易踩的坑:
- 使用 Lombok 的
@Data时,若字段是id,生成的 setter 是setId(),所以keyProperty="id"正确;但若字段叫orderNo,Lombok 生成setOrderNo(),就不能写成"order_no" - 嵌套对象不支持,
keyProperty="address.id"会静默失败,MyBatis 不报错也不赋值 - 批量插入(
<foreach></foreach>)时,useGeneratedKeys仍可用,但每个对象的 ID 会按顺序回填,前提是驱动和数据库支持批量获取 keys(MySQL 5.7+ OK,旧版可能只返回第一个)
MySQL 8.0 + MyBatis 3.4+ 下 useGeneratedKeys 仍无效?查 rewriteBatchedStatements
这个参数看着和主键无关,但它会影响 JDBC 是否真正执行 getGeneratedKeys()。当开启批量插入优化(rewriteBatchedStatements=true)时,MySQL 驱动会把多条 insert 合并为一条,导致无法逐条取 key —— 这时 useGeneratedKeys 就失效了。
解决方式很简单,在 JDBC URL 里关掉它:
jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=false
其他影响点:
- Oracle 需用
<selectkey></selectkey>,因为其 JDBC 不支持getGeneratedKeys(),useGeneratedKeys对 Oracle 完全无效 - SQL Server 要确保主键列定义为
IDENTITY(1,1),且驱动版本 ≥ 6.4 - 如果用了 MyBatis-Plus,
useGeneratedKeys会被自动处理,但底层仍走同一套逻辑,排查时别跳过 XML 层
为什么日志显示 SQL 执行成功,但 keyProperty 没被赋值?
最常被忽略的一点:MyBatis 默认用 ExecutorType.SIMPLE,但如果项目里手动配了 CachingExecutor 或二级缓存干扰,或者用了 @SelectKey 冲突,就可能导致回填被跳过。更隐蔽的是——对象传入时用了 final 字段或不可变类(如 record),setter 根本不存在,MyBatis 不报错,只是默默跳过赋值。
快速验证方法:
- 打开 MyBatis 日志(
log4j.logger.org.apache.ibatis=DEBUG),搜Returning generated key,没这句说明根本没触发回填流程 - 把
keyProperty改成一个明显不存在的字段名(如"xxxId"),看会不会报Reflector exception—— 如果不报,说明 MyBatis 根本没尝试反射赋值,大概率是对象构造/传参方式有问题 - 检查是否误用了
selectKey标签,它和useGeneratedKeys是互斥策略,同时存在时后者会被忽略
复杂点在于:回填发生在 Statement 执行之后、事务提交之前,所以即使你开了事务,ID 也已经写进对象里了;但如果你在 insert 后立刻做了一个无事务的查询,可能查不到刚插的记录——这不是 MyBatis 的问题,是隔离级别或刷盘时机导致的。










