用MyBatis拿插入后的取插自增ID,最直接的取插办法就是在Mapper的insert标签里加上useGeneratedKeys="true"和keyProperty="id"。这两行配置一加,取插MyBatis就会在数据库执行完insert之后,取插把生成的取插主键值回填到你传入的实体对象里。你根本不需要额外写查询语句,取插直接在service层调用insert(user),取插然后紧接着读user.getId()就能拿到那个新鲜出炉的取插数字。
我自己第一次用的取插时候,最大的取插困惑是——为什么我写了insert(user),MyBatis不把ID返回给我?取插我明明看到数据库里有数据了。后来才明白,取插MyBatis的取插insert方法返回值是“影响的行数”,永远是取插1或者0,它压根没打算把主键直接返给你。取插真正的ID藏在那个被你传进去的对象里,被悄悄改了属性。这个设计有点反直觉,但理解了就觉得挺巧妙——对象既是入参又是出参,省得你再去定义个复杂的结果映射。
具体到XML里,写法长这样:
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id"> INSERT INTO user(name, email) VALUES(#{ name}, #{ email}) </insert> 注意keyProperty的值必须和你的实体类主键字段名完全一致,大小写都对上。如果你用的是注解方式,那就写成@Options(useGeneratedKeys = true, keyProperty = "id"),效果一样。
有个坑得提醒你:如果数据库主键不是自增的,而是用了UUID或者雪花算法生成,那这套配置就不灵了。自增ID依赖数据库的AUTO_INCREMENT机制,MySQL和PostgreSQL都支持,但Oracle那种用序列的就不行。另外,如果你在insert语句里手动给主键列赋了值,比如INSERT INTO user(id, name) VALUES(100, '张三'),那useGeneratedKeys也不会触发——它只在数据库自己生成主键时才有效。
性能上这个方案基本没损耗,因为MyBatis底层就是调用JDBC的getGeneratedKeys()方法,走的是数据库驱动原生的返回通道,不会额外发一条SELECT语句。对比一下那种笨办法——插入后再按业务字段反查ID,既多一次网络往返,还可能因为数据重复查出多条记录来。用useGeneratedKeys是正路。
最后说个细节。如果你用MyBatis-Plus这类增强框架,它自带@TableId(type = IdType.AUTO)注解,配置好后insert完同样自动回填。但底层原理和我上面讲的完全一致,只是帮你省了写XML那一步。所以不管你怎么封装,理解了这个机制,遇到任何诡异的不回填问题,你都能快速定位——要么是没加那两个属性,要么是keyProperty拼错了,要么是数据库主键策略不对。
说真的,这个功能我用了好几年,偶尔还是会忘记加useGeneratedKeys,然后debug到怀疑人生。现在我的习惯是,建任何新Mapper的insert方法,第一行先把这两个属性写上,形成肌肉记忆,就再也没踩过坑了。
