欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-26 21:47:52
如何通过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,phon...

如何通过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表即可看到结果。
useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)

useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)
这里第二张图中的值就是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());这里则是关键代码,当进行批量操作时进行调用。我们看里面的具体实现。
useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)
说明:也可直接查询,直接全文搜索即可。
useGeneratedKeys,如何通过mybatis设置自生成主键(插入操作)

说明:这里执行逻辑显而易见,首先是获取本次批量插入的影响行数,然后再执行具体的获取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

相关标签: mysql mybatis sql