Original / Zhu Jiqian
The current version of mybatis plus is 3.0. As for whether the latest version does not have this problem, we will consider it later.
One day, I checked the code written by a former colleague and found that the corresponding table had auto set_ Increment increases automatically, but the data primary key id generated by the new function of the page is very strange. The length reaches 19 bits and does not increase from 1——

I checked and found that the self incrementing primary key of the table has increased from 1468844351843872770——

This is very strange. At present, the data volume of this table is very small, and the primary key is set to auto_ Normally, the self increment id is still in the range of 1000, but it has become a long string of numbers.
The underlying ORM framework uses mybatis plus. I thought about it. It looks like an id automatically generated when inserting the database. As a result, MySql auto is not used by default_ Increment to generate id.
Therefore, it is decided to locate step by step. First print out the sql log for mybatis plus to see whether the insert statement automatically generates an id before inserting it into the database.
According to the online tutorial, I set enable sql print log at the corresponding mybatis plus configuration in the yaml file——
mybatis-plus: mapper-locations: classpath*:mapper/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
However, it is strange that the sql log is not printed during the operation. Therefore, at one moment, I suddenly feel that these guys may copy each other without verification. When springboot integrates logback, this setting alone has no effect.
Finally, the following configurations are added to yaml to print MP sql log information normally——
logging: level: com: zhu: test: mapper: debug
Next, after verification, it is found that during the insert operation, mybatis plus does automatically generate a number with a length of 19 as the id of the data, which is inserted into MySql. Although the MySql table is set to auto increment, it is affected by the id generated by mybatis plus of 1468844351843872769, resulting in the automatic increment of the next data to 1468844351843872770, which is too long, When doing index maintenance, it will affect the efficiency and occupy too much space. Therefore, this problem must be solved.

Here, it is determined that the id of this long number is automatically generated at the code level. Finally, it enters the corresponding entity class and finds that the id field of the mapping data table does not display and set the corresponding primary key generation strategy.
@Data @TableName("test") public class Test extends Model<Test> implements Serializable { private Long id; ...... }
Mybatis plus mainly has the following primary key generation strategies——
@Getter public enum IdType { /** * Database ID self increment */ AUTO(0), /** * The type is not set with primary key */ NONE(1), /** * User input ID * This type can be populated by registering its own auto fill plug-in */ INPUT(2), /* The following three types are automatically filled only when the inserted object ID is empty. */ /** * Globally unique ID (idWorker), 19 digits generated according to snowflake algorithm, long type */ ID_WORKER(3), /** * Globally unique ID (UUID) */ UUID(4), /** * String globally unique ID (the string representation of idworker), which generates a 19 bit string according to the snowflake algorithm, string */ ID_WORKER_STR(5); private int key; IdType(int key) { this.key = key; } }
It is verified here that when this setting is set, the self incremented id of the database can be generated normally, and the database auto can be used_ Increment has a self increasing effect from 1. Of course, idtype is actually used Auto is also OK——
@Data @TableName("test") public class Test extends Model<Test> implements Serializable { @TableId(value = "id", type = IdType.INPUT) private Long id; ...... }
According to Baidu online, when the mybatis plus entity class does not display and set the primary key policy, it will be generated by using the snowflake algorithm by default, that is, idtype ID_ Worker or idtype ID_ WORKER_ STR, whether it is 19 bits of long type or 19 bits of string, should be determined according to the field definition type.
snowflake The algorithm is Twitter Open source distributed ID Generation algorithm, the result is a long Type ID . Its core idea: use 41 bit As milliseconds, 10 bit As a machine ID(5bit Data center, 5 bit Machine ID),12bit As a serial number within milliseconds (meaning that each node can generate 4096 serial numbers per millisecond) ID),Finally, there is a sign bit, always 0.
Next, verify how the mybatis plus default primary key policy works.
When the mybatis plus project is started, the annotation entity class will be initialized and cached in the system Map.
Here, you only need to pay attention to the initTableInfo method in the TableInfoHelper class of the mybatis plus source code. This method will be called when the project starts, and then initialize all entity classes annotated with @ TableName. The logic of which policy is used to set the primary key is in the method initTableFields(clazz, globalConfig, tableInfo)——
public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) { TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz.getName()); if (tableInfo != null) { if (tableInfo.getConfigMark() == null && builderAssistant != null) { tableInfo.setConfigMark(builderAssistant.getConfiguration()); } return tableInfo; } /* If no cache information is obtained, the */ tableInfo = new TableInfo(); GlobalConfig globalConfig; if (null != builderAssistant) { tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace()); tableInfo.setConfigMark(builderAssistant.getConfiguration()); tableInfo.setUnderCamel(builderAssistant.getConfiguration().isMapUnderscoreToCamelCase()); globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration()); } else { // Compatibility test scenario globalConfig = GlobalConfigUtils.defaults(); } /* Initialize table name correlation */ initTableName(clazz, globalConfig, tableInfo); /* Initialization field correlation */ initTableFields(clazz, globalConfig, tableInfo); /* Put in cache */ TABLE_INFO_CACHE.put(clazz.getName(), tableInfo); /* Cache Lambda mapping */ LambdaUtils.createCache(clazz, tableInfo); return tableInfo; }
In the initTableFields method related to the initialization field, it will judge whether there is @ TableId annotation. If not, execute the initTableIdWithoutAnnotation method. As mentioned above, if the entity class id does not add @ TableId(value = "id", type = IdType.INPUT) , the default primary key policy will be taken. The judgment here whether there is @ TableId annotation is to judge whether the default primary key policy needs to be taken. As for how to set the default primary key, we can directly enter the initTableIdWithoutAnnotation method.
public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) { /* Database global configuration */ GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig(); List<Field> list = getAllFields(clazz); // Mark whether the primary key is read boolean isReadPK = false; // Does @ TableId annotation exist boolean existTableId = isExistTableId(list); List<TableFieldInfo> fieldList = new ArrayList<>(); for (Field field : list) { /* * Primary key ID initialization */ if (!isReadPK) { if (existTableId) { isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, clazz); } else { isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, clazz); } if (isReadPK) { continue; } } ...... } ...... }
initTableIdWithoutAnnotation method——
private static final String DEFAULT_ID_NAME = "id"; /** * <p> * Primary key property initialization * </p> * * @param tableInfo Table information * @param field field * @param clazz Entity class * @return true Continue the next attribute judgment and return continue; */ private static boolean initTableIdWithoutAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, Field field, Class<?> clazz) { //Get entity class field name String column = field.getName(); if (dbConfig.isCapitalMode()) { column = column.toUpperCase(); } //When the field name is id if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) { if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { tableInfo.setKeyRelated(checkRelated(tableInfo.isUnderCamel(), field.getName(), column)) //Set table policy .setIdType(dbConfig.getIdType()) .setKeyColumn(column) .setKeyProperty(field.getName()) .setClazz(field.getDeclaringClass()); return true; } else { throwExceptionId(clazz); } } return false; }
After debugging, you can see that if there is no @ TableId annotation to display and set the primary key policy, the default setting is ID_WORKER(3), that is, 19 digits will be generated according to the snowflake algorithm, and the type is long.

It can be further found that DbConfig here is an instance of GlobalConfig.DbConfig. After entering the DbConfig class, you can see that the database mapped by the original entity class is set here, and the primary key type is IdType.ID_WORKER by default.
@Data public static class DbConfig { /** * Database type */ private DbType dbType = DbType.OTHER; /** * Primary key type (default id#u worker) */ private IdType idType = IdType.ID_WORKER; /** * table prefix */ private String tablePrefix; /** * Table name and whether to use underline naming (default true: the default database table is named with underline) */ private boolean tableUnderline = true; /** * String Type field LIKE */ private boolean columnLike = false; /** * Uppercase naming */ private boolean capitalMode = false; /** * Table keyword key generator */ private IKeyGenerator keyGenerator; /** * Logically delete global values (default 1. Indicates deleted) */ private String logicDeleteValue = "1"; /** * Logical global value not deleted (default 0, indicating not deleted) */ private String logicNotDeleteValue = "0"; /** * Field validation policy */ private FieldStrategy fieldStrategy = FieldStrategy.NOT_NULL; }
As for how to generate snowflake algorithm id, it will not be introduced in detail here. The specific logic is in the populateKeys method of mybatisdefaultparamerhandler class. The core code is as follows——
protected static Object populateKeys(MetaObjectHandler metaObjectHandler, TableInfo tableInfo, MappedStatement ms, Object parameterObject, boolean isInsert) { if (null == tableInfo) { /* Do not handle */ return parameterObject; } /* Custom meta object fill controller */ MetaObject metaObject = ms.getConfiguration().newMetaObject(parameterObject); // Fill primary key if (isInsert && !StringUtils.isEmpty(tableInfo.getKeyProperty()) && null != tableInfo.getIdType() && tableInfo.getIdType().getKey() >= 3) { Object idValue = metaObject.getValue(tableInfo.getKeyProperty()); /* Custom ID */ if (StringUtils.checkValNull(idValue)) { if (tableInfo.getIdType() == IdType.ID_WORKER) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getId()); } else if (tableInfo.getIdType() == IdType.ID_WORKER_STR) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.getIdStr()); } else if (tableInfo.getIdType() == IdType.UUID) { metaObject.setValue(tableInfo.getKeyProperty(), IdWorker.get32UUID()); } } } ...... }
As mentioned earlier, the default primary key policy is IdType.ID_WORKER. Here is a judgment tableInfo.getIdType() == IdType.ID_WORKER. You can see from the code Debug that the setValue(tableInfo.getKeyProperty(), IdWorker.getId()) code of metaObject is used to fill in the value of annotation id.

The populated value is idworker The 1468970800437465089 returned by getid () is exactly 19 bits long, which means that the id value generated here is the last one we want to find.
IdWorker. The essence of getid () implementation is to implement the 64 bit self incrementing ID algorithm based on Snowflake, which refers to the Snowflake algorithm——
/** * <p> * Efficient GUID generation algorithm (sequence), 64 bit self increasing ID algorithm based on Snowflake< br> * Optimize open source projects http://git.oschina.net/yu120/sequence * </p> * * @author hubin * @since 2016-08-01 */ public class IdWorker { /** * Machine code of host and process */ private static final Sequence WORKER = new Sequence(); public static long getId() { return WORKER.nextId(); } public static String getIdStr() { return String.valueOf(WORKER.nextId()); } /** * <p> * Get '-' UUID * </p> */ public static synchronized String get32UUID() { return UUID.randomUUID().toString().replace(StringPool.DASH, StringPool.EMPTY); } }