useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)
如何通过mybatis设置自生成主键(插入操作)
本文只提供对于接口映射器实现方式的讨论(此外还有xml映射器配置以及settings元素中设置)
使用范例
/**
* @MethodName: insertUserData
* @Description: 插入user表
* @Param: [user]
* @return: int
* @Author: LKH
* @Date: 2020-12-09 14:33
*/
@Insert("insert into user(id,username,phone,address,addtime) "
+ “values (#{id},#{username},#{phone},#{address},#{addtime})”)
@Options(useGeneratedKeys=true, keyProperty=“id”, keyColumn=“id”)
public int insertUserData(User user);
说明
useGeneratedKeys=“true”时 , mybatis会将自增ID值填充到user对象中的 id (keyProperty指定)属性。这个则是再@Options注解中进行对应,即keyProperty与keyColumn进行一个对应。
原理
可以理解为,我们指定好了对应关系,那么此时插入数据,未填写id数据(id未为null),那么在正确配置了@Options()的情况下,就会先从数据库(以mysql为例)中获取AUTO_INCREMENT的值,然后将其加1,再次存入要插入的即将执行的插入SQL。
说明:对于这里的AUTO_INCREMENT,我们截图看一下,此处通过数据库图形化管理工具Navicat Premium进行查看,这里我们双击user表即可看到结果。
这里第二张图中的值就是AUTO_INCREMENT。
这里可能有人就对批量操作产生疑问了,批量插入怎么办,不要慌,看下文。
批量插入
批量插入的时候只会返回一个id,这个id值是第一个插入行的AUTO_INCREMENT值,而具体如何实现获取全部id的呢,我们可以看一下getGeneratedKeys的具体实现。这里我们直接看代码
public ResultSet getGeneratedKeys() throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
if (!this.retrieveGeneratedKeys) {
throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), "S1009", this.getExceptionInterceptor());
} else if (this.batchedGeneratedKeys == null) {
return this.lastQueryIsOnDupKeyUpdate ? (this.generatedKeysResults = this.getGeneratedKeysInternal(1L)) : (this.generatedKeysResults = this.getGeneratedKeysInternal());
} else {
String encoding = this.session.getServerSession().getCharacterSetMetadata();
int collationIndex = this.session.getServerSession().getMetadataCollationIndex();
Field[] fields = new Field[]{new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20)};
this.generatedKeysResults = this.resultSetFactory.createFromResultsetRows(1007, 1004, new ResultsetRowsStatic(this.batchedGeneratedKeys, new DefaultColumnDefinition(fields)));
return this.generatedKeysResults;
}
}
} catch (CJException var8) {
throw SQLExceptionsMapping.translateException(var8, this.getExceptionInterceptor());
}
}
说明:这里主要是客户端对于代码处理的一个方式,return this.lastQueryIsOnDupKeyUpdate ? (this.generatedKeysResults = this.getGeneratedKeysInternal(1L)) : (this.generatedKeysResults = this.getGeneratedKeysInternal());这里则是关键代码,当进行批量操作时进行调用。我们看里面的具体实现。
说明:也可直接查询,直接全文搜索即可。
说明:这里执行逻辑显而易见,首先是获取本次批量插入的影响行数,然后再执行具体的获取id操作。这里我们再看具体获取id的操作方式。
protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException {
try {
synchronized(this.checkClosed().getConnectionMutex()) {
String encoding = this.session.getServerSession().getCharacterSetMetadata();
int collationIndex = this.session.getServerSession().getMetadataCollationIndex();
Field[] fields = new Field[]{new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20)};
ArrayList<Row> rowSet = new ArrayList();
long beginAt = this.getLastInsertID();
if (this.results != null) {
String serverInfo = this.results.getServerInfo();
if (numKeys > 0L && this.results.getFirstCharOfQuery() == 'R' && serverInfo != null && serverInfo.length() > 0) {
numKeys = this.getRecordCountFromInfo(serverInfo);
}
if (beginAt != 0L && numKeys > 0L) {
for(int i = 0; (long)i < numKeys; ++i) {
byte[][] row = new byte[1][];
if (beginAt > 0L) {
row[0] = StringUtils.getBytes(Long.toString(beginAt));
} else {
byte[] asBytes = new byte[]{(byte)((int)(beginAt >>> 56)), (byte)((int)(beginAt >>> 48)), (byte)((int)(beginAt >>> 40)), (byte)((int)(beginAt >>> 32)), (byte)((int)(beginAt >>> 24)), (byte)((int)(beginAt >>> 16)), (byte)((int)(beginAt >>> 8)), (byte)((int)(beginAt & 255L))};
BigInteger val = new BigInteger(1, asBytes);
row[0] = val.toString().getBytes();
}
rowSet.add(new ByteArrayRow(row, this.getExceptionInterceptor()));
beginAt += (long)this.connection.getAutoIncrementIncrement();
}
}
}
ResultSetImpl gkRs = this.resultSetFactory.createFromResultsetRows(1007, 1004, new ResultsetRowsStatic(rowSet, new DefaultColumnDefinition(fields)));
return gkRs;
}
} catch (CJException var18) {
throw SQLExceptionsMapping.translateException(var18, this.getExceptionInterceptor());
}
}
说明:我们通过阅读这个函数就可以知道其主要实现方式:通过迭代影响的行数,然后依次获取id值。因此批量操作的insert也是可以正确获取值的。
不过这里我看了一些网上一些人的讨论,对于批量insertOrUpdate是会出现问题的,因为insertOrUpdate的影响行数不是插入的数据行数。比如我们插入5条数据,3条会update,2条会insert,这时候updateCount就是3+5=8了,那么generateid就会8个了,然后mybatis这里取前5个塞到数据里,那就会造成错误。
未经允许不可转载
本文地址:https://blog.csdn.net/weixin_44161378/article/details/111048691