MybatisPlus source code analysis 5: How insert generates the primary key

MybatisPlus source code analysis 5: How insert generates the primary key

    • 1. Question
    • 2. Execution process of ID generator
    • 3.ID generation and filling
    • 4.Default and specified idType

1. Question

Have you ever thought about a problem? Usually when saving data, the ID is not specified, but there is an ID in the inserted Sql. How does Mybatis-Plus help us generate the ID?

2. Execution process of ID generator

The execution process of the ID generator is exactly the same as that of the MetaObjectHandler. The parameters are processed when the StatementHandler is generated, because the functions of both are to fill in the parameters.

MeataObjectHandler execution process can be referred to: MybatisPlus source code analysis 3: Metadata Processor-MetaObjectHandler

private void process(Object parameter) {<!-- -->
    if (parameter != null) {<!-- -->
        TableInfo tableInfo = null;
        Object entity = parameter;
        if (parameter instanceof Map) {<!-- -->
            // When processing single parameters using annotation tags, try to extract et to obtain entity parameters.
            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) {<!-- -->
            //At this point, you should convert to the entity parameter object, because filling and ID processing are all processed for the entity object, and there is no need to pass the original parameter object.
            MetaObject metaObject = this.configuration.newMetaObject(entity);
            if (SqlCommandType.INSERT == this.sqlCommandType) {<!-- -->
                populateKeys(tableInfo, metaObject, entity);
                insertFill(metaObject, tableInfo);
            } else {<!-- -->
                updateFill(metaObject, tableInfo);
            }
        }
    }
}

As you can see, populateKeys(tableInfo, metaObject, entity) populates the primary key, while insertFill and updateFill populate the saved data attributes.

3.id generation and filling

protected void populateKeys(TableInfo tableInfo, MetaObject metaObject, Object entity) {<!-- -->
    final IdType idType = tableInfo.getIdType();
    final String keyProperty = tableInfo.getKeyProperty();
    if (StringUtils.isNotBlank(keyProperty) & amp; & amp; null != idType & amp; & amp; 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. Get the ID generator from the global configuration GlobalConfig. If there is no configuration, mp defaults to the DefaultIdentifierGenerator generator.
  2. Get the current primary key value, determine whether it is empty, and fill it if it is empty. If it is not empty, do nothing and use the primary key value of the parameter itself.
  3. Determine whether the type of id is ASSIGN_ID (Number type) or ASSIGN_UUID (String type). You can specify it here through @TableId#type
    3.1. If it is ASSIGN_ID, call the identifierGenerator#nextId method to generate the primary key value.
    3.2. If it is ASSIGN_UUID, call identifierGenerator#nextUUID to generate the primary key value.
  4. Put the primary key value into the parameter object through metaObject metaObject.setValue

4.Default and specified idType

The idType is determined when the process is started and tableInfo is initialized.
For the startup process, please refer to: MybatisPlus source code analysis 2: mybatis-plus startup process Sql injector-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()));
     }
     /* Primary key strategy (Annotation > Global) */
     //Set Sequence and other strategies are invalid
     if (IdType.NONE == tableId.type()) {<!-- -->
         tableInfo.setIdType(dbConfig.getIdType());
     } else {<!-- -->
         tableInfo.setIdType(tableId.type());
     }

     /* Field */
     String column = property;
     if (StringUtils.isNotBlank(tableId.value())) {<!-- -->
         column = tableId.value();
     } else {<!-- -->
         // Enable field underline declaration
         if (underCamel) {<!-- -->
             column = StringUtils.camelToUnderline(column);
         }
         // Global uppercase naming
         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! This is not recommended. Please use a wrapper class 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. The type in the @TableId annotation defaults to IdType.NONE
  2. If it is IdType.NONE, then take dbConfig.getIdType() in the global configuration, and the global configuration defaults to IdType.ASSIGN_ID. So the default is IdType.ASSIGN_ID
  3. If it is not IdType.NONE, take the type specified in @TableId