MybatisPlus源码解析5:insert如何生成主键

MybatisPlus源码解析5:insert如何生成主键

1. 问题

有没有想过一个问题,平时在保存数据的时候,没有指定id,但是在插入的Sql中有id,Mybatis-Plus是如何帮我们生成的ID

2. ID生成器的执行流程

ID生成器的执行流程与MetaObjectHandler的执行流程一模一样,都是在生成StatementHandler的时候对参数进行处理,因为两者的功能都是对参数进行填充。

MeataObjectHandler执行流程可参考:MybatisPlus源码解析3:元数据处理器-MetaObjectHandler

private void process(Object parameter) {
    if (parameter != null) {
        TableInfo tableInfo = null;
        Object entity = parameter;
        if (parameter instanceof Map) {
            // 处理单参数使用注解标记的时候,尝试提取et来获取实体参数
            Map<?, ?> map = (Map<?, ?>) parameter;
            if (map.containsKey(Constants.ENTITY)) {
                Object et = map.get(Constants.ENTITY);
                if (et != null) {
                    entity = et;
                    tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
                }
            }
        } else {
            tableInfo = TableInfoHelper.getTableInfo(parameter.getClass());
        }
        if (tableInfo != null) {
            //到这里就应该转换到实体参数对象了,因为填充和ID处理都是针对实体对象处理的,不用传递原参数对象下去.
            MetaObject metaObject = this.configuration.newMetaObject(entity);
            if (SqlCommandType.INSERT == this.sqlCommandType) {
                populateKeys(tableInfo, metaObject, entity);
                insertFill(metaObject, tableInfo);
            } else {
                updateFill(metaObject, tableInfo);
            }
        }
    }
}

可以看到,populateKeys(tableInfo, metaObject, entity)是对主键进行填充,而insertFill和updateFill是对保存的数据属性进行填充

3.id生成与填充

protected void populateKeys(TableInfo tableInfo, MetaObject metaObject, Object entity) {
    final IdType idType = tableInfo.getIdType();
    final String keyProperty = tableInfo.getKeyProperty();
    if (StringUtils.isNotBlank(keyProperty) && null != idType && idType.getKey() >= 3) {
        final IdentifierGenerator identifierGenerator = GlobalConfigUtils.getGlobalConfig(this.configuration).getIdentifierGenerator();
        Object idValue = metaObject.getValue(keyProperty);
        if (identifierGenerator.assignId(idValue)) {
            if (idType.getKey() == IdType.ASSIGN_ID.getKey()) {
                Class<?> keyType = tableInfo.getKeyType();
                if (Number.class.isAssignableFrom(keyType)) {
                    Number id = identifierGenerator.nextId(entity);
                    if (keyType == id.getClass()) {
                        metaObject.setValue(keyProperty, id);
                    } else if (Integer.class == keyType) {
                        metaObject.setValue(keyProperty, id.intValue());
                    } else if (Long.class == keyType) {
                        metaObject.setValue(keyProperty, id.longValue());
                    } else if (BigDecimal.class.isAssignableFrom(keyType)) {
                        metaObject.setValue(keyProperty, new BigDecimal(id.longValue()));
                    } else if (BigInteger.class.isAssignableFrom(keyType)) {
                        metaObject.setValue(keyProperty, new BigInteger(id.toString()));
                    } else {
                        throw new MybatisPlusException("Key type '" + keyType + "' not supported");
                    }
                } else if (String.class.isAssignableFrom(keyType)) {
                    metaObject.setValue(keyProperty, identifierGenerator.nextId(entity).toString());
                } else {
                    metaObject.setValue(keyProperty, identifierGenerator.nextId(entity));
                }
            } else if (idType.getKey() == IdType.ASSIGN_UUID.getKey()) {
                metaObject.setValue(keyProperty, identifierGenerator.nextUUID(entity));
            }
        }
    }
}
  1. 从全局配置GlobalConfig中获取ID生成器,如果没有配置,mp默认的是DefaultIdentifierGenerator生成器
  2. 获取到当前的主键的值,判断是否为空,如果为空就去填充。不为空就什么也不做,使用参数本身的主键值
  3. 判断id的类型为ASSIGN_ID(Number类型)还是ASSIGN_UUID(String类型),这里可以通过@TableId#type去指定
    3.1. 如果是ASSIGN_ID,就调用identifierGenerator#nextId方法生成主键值
    3.2. 如果是ASSIGN_UUID,就调用identifierGenerator#nextUUID生成主键值
  4. 通过metaObject将主键值到参数对象里面 metaObject.setValue

4.默认和指定idType

idType是在启动流程,初始化tableInfo的时候确定的。
启动流程可参考:MybatisPlus源码解析2:mybatis-plus启动流程之Sql注入器-SqlInjector

private static void initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, Field field, TableId tableId) {
    boolean underCamel = tableInfo.isUnderCamel();
     final String property = field.getName();
     if (field.getAnnotation(TableField.class) != null) {
         logger.warn(String.format("This \"%s\" is the table primary key by @TableId annotation in Class: \"%s\",So @TableField annotation will not work!",
             property, tableInfo.getEntityType().getName()));
     }
     /* 主键策略( 注解 > 全局 ) */
     // 设置 Sequence 其他策略无效
     if (IdType.NONE == tableId.type()) {
         tableInfo.setIdType(dbConfig.getIdType());
     } else {
         tableInfo.setIdType(tableId.type());
     }

     /* 字段 */
     String column = property;
     if (StringUtils.isNotBlank(tableId.value())) {
         column = tableId.value();
     } else {
         // 开启字段下划线申明
         if (underCamel) {
             column = StringUtils.camelToUnderline(column);
         }
         // 全局大写命名
         if (dbConfig.isCapitalMode()) {
             column = column.toUpperCase();
         }
     }
     final Class<?> keyType = tableInfo.getReflector().getGetterType(property);
     if (keyType.isPrimitive()) {
         logger.warn(String.format("This primary key of \"%s\" is primitive !不建议如此请使用包装类 in Class: \"%s\"",
             property, tableInfo.getEntityType().getName()));
     }
     if (StringUtils.isEmpty(tableId.value())) {
         String columnFormat = dbConfig.getColumnFormat();
         if (StringUtils.isNotBlank(columnFormat)) {
             column = String.format(columnFormat, column);
         }
     }
     tableInfo.setKeyRelated(checkRelated(underCamel, property, column))
         .setKeyColumn(column)
         .setKeyProperty(property)
         .setKeyType(keyType);
 }
  1. @TableId注解中的type默认为IdType.NONE
  2. 如果为IdType.NONE,那么就取全局配置中的dbConfig.getIdType(),而全局配置默认为IdType.ASSIGN_ID。所以就是默认为IdType.ASSIGN_ID
  3. 如果不为IdType.NONE,就取@TableId中指定的type
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值