MyBatisPlus批量插入主键被覆盖问题解决
背景
通过MybatisPlus实现insertBatchSomeColumn
接口实现真正意义上的批量插入时,数据已经准备了Id但是插入进数据库时Id被覆盖。
原因
查看源码可以发现,在处理中如果Id字段通过@TableId(type = IdType.AUTO)
注解标记时,拼接的字符串会忽略Id字段的值,采用数据库自增进行填充。
@SuppressWarnings("Duplicates")
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true, null, false) +
this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) +
this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (tableInfo.havePK()) {
if (tableInfo.getIdType() == IdType.AUTO) {
/* 自增主键 */
keyGenerator = Jdbc3KeyGenerator.INSTANCE;
keyProperty = tableInfo.getKeyProperty();
// 去除转义符
keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(this.methodName, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
}
例子
有一个Task,作用是在项目启动时扫描项目中的接口(被接口注解标注)信息,通过判断是否被标记鉴权注解来进行数据库插入。所以此时
Id
是通过接口关系指定的,即一对多的关系。
- 当注解为
@TableId(type = IdType.AUTO)
时
SQL日志:
JDBC Connection [ConnectionProxyImpl{connectedTime=2024-06-07 16:44:15.881, closeCount=0, lastValidateTimeMillis=2024-06-07 16:44:15.887}] will not be managed by Spring
==> Preparing: INSERT INTO sys_api (parent_id,operation,url,summary,method,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?)
==>
SQL拦截:
INSERT INTO sys_api (parent_id,operation,url,summary,method,status,create_time,update_time) VALUES xxxxxx
数据库数据:
- 当注解为非
@TableId(type = IdType.AUTO)
时,如@TableId(type = IdType.ASSIGN_ID)
雪花Id
SQL日志:
DBC Connection [ConnectionProxyImpl{connectedTime=2024-06-07 16:47:17.421, closeCount=0, lastValidateTimeMillis=2024-06-07 16:47:17.426}] will not be managed by Spring
==> Preparing: INSERT INTO sys_api (id,parent_id,operation,url,summary,method,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?) , (?,?,?,?,?,?,?,?,?)
==>
SQL拦截:
INSERT INTO sys_api (id,parent_id,operation,url,summary,method,status,create_time,update_time) VALUES xxxxxx
数据库数据:
总结
在有需要用到数据批量插入场景时,如果使用
insertBatchSomeColumn
进行数据插入,需要修改主键Id类型。特别是在使用到数据导入等功能使用到批量插入时,要注意Id被覆盖的问题。