Mybatis 可以在添加数据时插入时候,返回自增主键(mysql),现在分析一下其中的原理,并说明mysql的LAST_INSERT_ID 如何做到线程安全的。
1 | 有下列两种方式 |
这两种原理并不相同。
对于第一种方式,我们知道mybatis只是对JDBC的封装而已,我们看一下JDBC返回自增主键。
1 | String insertsSql = "INSERT INTO tbl_zname (`name`) VALUES (?)"; |
到这里为止了吗?肯定不是,既然分析原理,那就深入一点。其实返回自增id,是mysql协议里的。我们抓包看一下。
查询报文:单条插入
响应报文:单条插入
可以看到响应报文中,有一条Last INSERT ID,这个字段即为插入的自增主键。
第二种方式,注意 order=”AFTER”,如果是mysql的自增主键则是after,代表数据插入后再查询。如果是Oracle或者非自增主键如UUID,则是before,代表在插入数据前先生成主键,但是此种方法并不常见,因为如果不是数据库生成主键,代码上也可以生成主键,不需要额外的查询。
对于第二种方式,其本质是二个SQL语句复合而成。
很容易理解,先插入数据,再查询主键。但是有一个问题就产生了,如果是并发情况下,插入和查询,是两个步骤,明显不是原子操作,怎么能保证数据的正确性呢?
LAST_INSERT_ID 函数并不是获取数据库最新的id,LAST_INSERT_ID函数返回的是Last_INSERT_ID这个值,实际上这个值记录在mysql连接中,也就是说LAST_INSERT_ID是连接隔离的,而mysql的网络模型,为一个线程对于一个连接,所以每个连接执行insert语句的时候,只是在本线程中记录了当前线程的Last_INSERT_ID,并返回。之后这个连接如果没有新的inert语句,这个值保持不变。所以这就是连接绑定线程,线程绑定变量,相当于线程局部变量,当然没有线程安全问题。
那对于批量添加生成自增id又是怎么样的呢?难道说返回的是一个列表吗?
其实对于批量添加只能用1的方式进行生成自增id,这个也是JDBC自带的功能。原理其实隐藏在第二幅图中。不管对于单条添加还是批量添加,返回的报文是一样的。只是Last_INSERT_ID并不是最后一条插入的id,而是最后一条插入语句【包括批量插入】的第一条插入id,Affected Rows就是插入条数,所有生成的id为[Last_INSERT_ID, Last_INSERT_ID +Affected Rows) 区间所有整数。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!