useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)

useGeneratedKeys,如何通过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的值,存入要插入的即将执行的插入SQL。
说明:对于这里的AUTO_INCREMENT,我们截图看一下,此处通过数据库图形化管理工具Navicat Premium进行查看,这里我们双击user表即可看到结果。
1
主键值

2
这里第二张图中的值就是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) {//此处为批量时的操作,也就是批处理生成自增长keys为空
                    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)));//这里我的理解就是获取自增长keys结果集
                    return this.generatedKeysResults;//这里就返回多个自增长key
                }
            }
        } catch (CJException var8) {
            throw SQLExceptionsMapping.translateException(var8, this.getExceptionInterceptor());
        }
    }

说明:这里主要是客户端对于代码处理的一个方式,return this.lastQueryIsOnDupKeyUpdate ? (this.generatedKeysResults = this.getGeneratedKeysInternal(1L)) : (this.generatedKeysResults = this.getGeneratedKeysInternal());这里则是关键代码,当进行批量操作时进行调用。我们看里面的具体实现。
1引用
说明:也可直接查询,直接全文搜索即可。
4

说明:这里执行逻辑显而易见,首先是获取本次批量插入的影响行数,然后再执行具体的获取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个塞到数据里,那就会造成错误。

未经允许不可转载

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值