首页 > 其他分享 >mybatis获取insert操作自增主键值原理

mybatis获取insert操作自增主键值原理

时间:2023-12-20 23:55:43浏览次数:50  
标签:insert beginAt 获取 键值 mybatis new byte 主键 asBytes

大家好,我是joker,希望你快乐。

上一篇mybatis insert操作获取自增主键中介绍了如何获取主键值,接下来这篇我们将通过跟踪源码的方式进一步探究mybatis是如何获取到主键的。

其实上一篇中,通过官方文档我们可以看出mybatis还是通过 JDBC 的 getGeneratedKeys 方法获取由数据库内部生成的主键。

  • useGeneratedKeys

    (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。

  • keyProperty

    (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。

为了对这个过程有一个更加清晰的认识,下面我们开始跟踪主要代码并做简单说明。

  1. 跟踪了一路代理,模板,执行器中的方法后,跟踪到了PreparedStatementHandler.java中的update方法。具体代码如下:
  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }
  1. 可以看到getKeyGenerator()获取主键生成器,进一步跟踪,会进入到Jdbc3KeyGenerator.java的processBatch方法。
  public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
    final String[] keyProperties = ms.getKeyProperties();
    if (keyProperties == null || keyProperties.length == 0) {
      return;
    }
    try (ResultSet rs = stmt.getGeneratedKeys()) {
      final ResultSetMetaData rsmd = rs.getMetaData();
      final Configuration configuration = ms.getConfiguration();
      if (rsmd.getColumnCount() < keyProperties.length) {
        // Error?
      } else {
        assignKeys(configuration, rs, rsmd, keyProperties, parameter);
      }
    } catch (Exception e) {
      throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
    }
  }

可以看到mybatis获取数据库生成的id方法还是通过jdbc的驱动的实现类进行获取的。

  1. 我使用的是mysql,最终会进入到mysql的jdbc驱动中StatementImpl.java实现类。如果使用其他数据也会进入对应jdbc驱动的StatementImpl.java实现类,获取自增主键需要数据库及jdbc驱动支持。
protected ResultSetInternalMethods getGeneratedKeysInternal(long numKeys) throws SQLException {
    synchronized (checkClosed().getConnectionMutex()) {
        String encoding = this.session.getServerSession().getCharsetSettings().getMetadataEncoding();
        int collationIndex = this.session.getServerSession().getCharsetSettings().getMetadataCollationIndex();
        Field[] fields = new Field[1];
        fields[0] = new Field("", "GENERATED_KEY", collationIndex, encoding, MysqlType.BIGINT_UNSIGNED, 20);

        ArrayList<Row> rowSet = new ArrayList<>();

        long beginAt = getLastInsertID();

        if (this.results != null) {
            String serverInfo = this.results.getServerInfo();

            //
            // Only parse server info messages for 'REPLACE' queries
            //
            if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R') && (serverInfo != null) && (serverInfo.length() > 0)) {
                numKeys = getRecordCountFromInfo(serverInfo);
            }

            if ((beginAt != 0 /* BIGINT UNSIGNED can wrap the protocol representation */) && (numKeys > 0)) {
                for (int i = 0; i < numKeys; i++) {
                    byte[][] row = new byte[1][];
                    if (beginAt > 0) {
                        row[0] = StringUtils.getBytes(Long.toString(beginAt));
                    } else {
                        byte[] asBytes = new byte[8];
                        asBytes[7] = (byte) (beginAt & 0xff);
                        asBytes[6] = (byte) (beginAt >>> 8);
                        asBytes[5] = (byte) (beginAt >>> 16);
                        asBytes[4] = (byte) (beginAt >>> 24);
                        asBytes[3] = (byte) (beginAt >>> 32);
                        asBytes[2] = (byte) (beginAt >>> 40);
                        asBytes[1] = (byte) (beginAt >>> 48);
                        asBytes[0] = (byte) (beginAt >>> 56);

                        BigInteger val = new BigInteger(1, asBytes);

                        row[0] = val.toString().getBytes();
                    }
                    rowSet.add(new ByteArrayRow(row, getExceptionInterceptor()));
                    beginAt += this.connection.getAutoIncrementIncrement();
                }
            }
        }

        ResultSetImpl gkRs = this.resultSetFactory.createFromResultsetRows(ResultSet.CONCUR_READ_ONLY, ResultSet.TYPE_SCROLL_INSENSITIVE,
                new ResultsetRowsStatic(rowSet, new DefaultColumnDefinition(fields)));

        return gkRs;
    }
}

可以看到getLastInsertID()方法获取了最后一次插入的id,这个方法只是读取了最后的自增主键,通过注释可以看出select LAST_INSERT_ID()使用这种方式有并发问题,那具体是如何为这个lastInsertId赋值的我们接着说。

/**
 * getLastInsertID returns the value of the auto_incremented key after an
 * executeQuery() or excute() call.
 * 
 * <p>
 * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()" which is tied to the Connection that created this Statement, and therefore could
 * have had many INSERTS performed before one gets a chance to call "select LAST_INSERT_ID()".
 * </p>
 * 
 * @return the last update ID.
 */
public long getLastInsertID() {
    synchronized (checkClosed().getConnectionMutex()) {
        return this.lastInsertId;
    }
}

至此,我们验证了MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键。

在JDBC中是通过mysql协议进行通讯获取的,在这里做一个简单说明,有兴趣可以去看代码。

MySQL: Client/Server Protocol

MySQL协议分析 - davygeek - 博客园 (cnblogs.com)

标签:insert,beginAt,获取,键值,mybatis,new,byte,主键,asBytes
From: https://www.cnblogs.com/Crazy_Joker/p/17917908.html

相关文章

  • 【SpringBootWeb入门-16】Mybatis-基础操作-多条件查询操作&XML文件配置SQL
    1、章节回顾上一篇文章我们讲解了Mybatis的增改查操作,本篇继续学习Mybatis的复杂查询操作(多条件查询)。2、增删改查操作-多条件查询操作根据条件姓名、性别、入职时间来查询员工表emp数据,其中员工姓名支持模糊匹配,性别进行精确匹配,入职时间进行范围查询,查询结果按照最后修改时间......
  • MybatisPlus
    Mybatis-Plus1.0简介MyBatis-Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为了简化开发、提升效率而生。MyBatis-Plus提供了通用的mapper和service,可以在不编写任何SQL语句的情况下,快速的实现对单表的CRUD、批量、逻辑删除、分页等操作官网:https://baomidou.......
  • MybatisPlus配置逻辑删除
    1、步骤1:配置逻辑删除的信息全局配置mybatis-plus:global-config:db-config:logic-delete-field:flag#全局逻辑删除的实体字段名(since3.3.0,配置后可以忽略不配置步骤2)logic-delete-value:1#逻辑已删除值(默认为1)logic-not-delete......
  • SpringBoot原理分析 | Spring Data整合:JDBC、Druid、Mybatis
    ......
  • 解决MyBatis-Plus 更新字段为null 不生效
    1.异常说明:mapper.updateById()时,set为null未生效,其他字段更新periodRecordOriginal.setSettleTime(null);periodRecordOriginal.setActualSettleTime(null);periodRecordOriginal.setSettleStatus(0);i......
  • 【SpringBootWeb入门-15】Mybatis-基础操作-增改查操作
    1、章节回顾上一篇文章我们讲解了Mybatis的删除操作,本篇继续学习Mybatis的新增操作:根据员工表字段,新增员工表的数据,新增的字段有:用户名、员工姓名、性别、图像、职位、入职日期、归属部门。2、增删改查操作-新增操作员工表emp新增数据,对应的SQL语句:insertintoemp(username......
  • 61道MyBatis高频题整理(附答案背诵版)
    1、介绍下MyBatis?MyBatis是一个基于Java的持久层框架,它封装了底层的JDBC操作,大大简化了数据库操作的复杂性。MyBatis的主要特点包括:SQL语句与Java代码的分离:MyBatis允许你直接在XML文件中编写SQL语句,这样可以将SQL语句从Java代码中分离出来,使得代码更加清晰,易于维护。强大......
  • mybatis获取insert操作自增主键值
    大家好,我是joker,希望你快乐。在日常使用mybatis时,经常会遇到数据库表的主键是自增id的情况。数据库的表结构又设计成主子表的情况,在插入主表数据后,为了维护主子表关系,通常需要获取插入主表中的自增id。代码示例mapper.xml<insertid="insertCity"parameterType="City"useG......
  • 最近在使用SpringBoot整合MyBatis-Plus时出现的问题
    版本信息:IDEA2022、jdk17、maven3.8.6、SpringBoot3+MyBatis-Plus依赖版本信息:<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3</version&g......
  • 14. 现在有个外键值是area_id_id,我就想他叫area_id该怎么做
     如果你想将一个外键字段的数据库列名从默认的area_id_id更改为area_id,你可以使用db_column参数来指定自定义的数据库列名。以下是一个示例:pythonCopycodefromdjango.dbimportmodelsclassYourModel(models.Model):  area=models.ForeignKey(Area,on_delete=model......