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));
}
}
}
}
- 从全局配置GlobalConfig中获取ID生成器,如果没有配置,mp默认的是DefaultIdentifierGenerator生成器
- 获取到当前的主键的值,判断是否为空,如果为空就去填充。不为空就什么也不做,使用参数本身的主键值
- 判断id的类型为ASSIGN_ID(Number类型)还是ASSIGN_UUID(String类型),这里可以通过@TableId#type去指定
3.1. 如果是ASSIGN_ID,就调用identifierGenerator#nextId方法生成主键值
3.2. 如果是ASSIGN_UUID,就调用identifierGenerator#nextUUID生成主键值 - 通过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);
}
- @TableId注解中的type默认为IdType.NONE
- 如果为IdType.NONE,那么就取全局配置中的dbConfig.getIdType(),而全局配置默认为IdType.ASSIGN_ID。所以就是默认为IdType.ASSIGN_ID
- 如果不为IdType.NONE,就取@TableId中指定的type