SQL 语句执行insert,这谁都会,执行之后,返回结果是影响行数。但是在我们应用开发场景中,有些表的主键采用自增生成。这个时候,你怎么能把这个自增的主键值拿到呢?
你说这还不简单,马上再执行一下select,就都查出来啦。 你的where是什么才限定刚好是新增的那一条呢? 你说,那我查主键值最大的那一条呢? 也不灵。毕竟insert和select之间是有时间差的,此时可能有其他写入操作。
一些常用的 ORM 框架在执行完save操作之后,一般都会把新生成的主键自动回填到PO对象中。那它们是怎么做到的呢?
带着这个疑问,咱们一起来看一下,时下活跃在各大应用中的明星MyBatis。
MyBatis 是如何取到自增主键值的
我们知道, MyBatis 无论通过注解,还是XML配置的形式,将待执行的SQL包装起来,在执行时通过代理进行解析。
我们分析过MyBatis的大致执行原理,为何只写一个接口就能映射到一个XML或者注解中对应的SQL逻辑上
其中有个类MappedStatement,我们在XML里配置的各类SQL 都会转成这样一个实例。这个类中包含了大量配置相关的信息。
我们摘一段代码来看看:
代码比较长,咱们长话短说,重点看这一句
这里根据配置时指定是否使用UseGeneratedKeys来决定生成的keyGenerator实例是谁。
咱们使用时,在 XML 里的 insert 一般写成这样:
这个时候, insert 这个 XML 标签包含的一些配置就会使用默认值,其中有一个配置项就是咱们上面在MappedStatement中看到的useGeneratedKeys。它的默认值是false。也就是说,这种配置情况下,这个插入操作的自增主键并不会回写回来。
这个配置主要作用:
(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
那咱们把它改成true,同时把自增的列和对应的PO里的属性名写上就可以了。再执行一次操作,在insert 之后, PO 里对应的主键就已经自动填充了。
他是在哪一步做的呢?
MyBatis 如何从 XML的方法映射到 SQLCommand,然后从SQLCommand 的SQLType 再执行不同的操作这些,咱们不细说,具体INSERT,也会执行到PreparedStatementHandler类中的这个update方法。
请留意KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();这一句,就是咱们在创建mappedStatement时,根据 useKeyGenerator配置生成的。
在keyGenerator.processAfter方法中,会有一个操作。这里的keyGenerator是根据配置来决定的,如果没有配置useGeneratedKeys=true的话,那这里返回的就是NoKeyGenerator,配置的话,则返回Jdbc3KeyGenerator
位于 Jdbc3KeyGenerator类中, 我们看到,还是继续持有执行SQL的 Statement,然后 通过执行其getGeneratedKeys方法,来拿到一些数据。
重点就是这一句:rs = stmt.getGeneratedKeys();。
Java 文档里描述如下:
Retrieves any auto-generated keys created as a result of executing thisStatementobject. If thisStatementobject did not generate any keys, an emptyResultSetobject is returned.
和我们在前面看到对于useGeneratedKeys的说明描述一致,即通过JDBC API 的方法来拿到自增生成的值,在拿到rs,也就是标准的 JDBC ResultSet 之后,操作和一般的JDBC一样,通过读取columnName来取值。所以我们在前面配置的地方,也需要加上keyColumn。
这就是 MyBatis 获取自增主键的全部秘密。
欢迎工作一到五年的Java工程师朋友们加入Java填坑之路:860113481
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!