MyBatis-Plus primary key automatically generates source code analysis (and problem primary key generation failure?) |
reason:
When testing an added function today, an error was reported to me: (as follows)
### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Column 'id' cannot be null
A very common problem, the id field of the database is the primary key is not null, it is empty when inserted. Constraint restrictions cause this exception to be thrown.
Ideas:
1. First check the annotations that should be added:
@TableName("数据库表名")
public class 类名 implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 自增记录
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
@JsonSerialize(using = ToStringSerializer.class)
private Long id; //可以为 String 或者 Long
2. Then check whether the corresponding fields in the database match. ASSIGN_ID means that the number of digits generated by the snowflake algorithm is relatively large, and it needs to be stored in bigint . (There is no problem after checking)
3. Then check the yml configuration:
mybatis-plus:
global-config:
db-config:
id-type: ASSIGN_UUID #也可以用3表示
No problem, because adding this configuration to the previous code has no effect, maybe the priority of the code is higher than the configuration (my guess.)
This is a headache, and it’s all right. Why is it empty? Later, I cleared the compiled file and recompiled, and then tried other primary key generation strategies, and there was no problem, except for ASSIGN_ID, which suddenly felt targeted. The series of operations are still useless. Baidu has no results (no similar problems).
So I had to track the source code to see what was going on.
1. Find the property and set a breakpoint, so I came to the MybatisDefaultParameterHandler class, which is used to handle the default parameter assignment.
There are many methods in it, but the method I need to find is this:
/**
* 填充主键
*
* @param tableInfo 数据库表反射信息
* @param metaObject 元数据对象
* @param entity 实体信息
*/
protected static 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(tableInfo.getConfiguration()).getIdentifierGenerator();
Object idValue = metaObject.getValue(keyProperty);
if (StringUtils.checkValNull(idValue)) {
if (idType.getKey() == IdType.ASSIGN_ID.getKey()) {
if (Number.class.isAssignableFrom(tableInfo.getKeyType())) {
metaObject.setValue(keyProperty, identifierGenerator.nextId(entity)); //这个方法生成ID然后赋值
} else {
metaObject.setValue(keyProperty, identifierGenerator.nextId(entity).toString());
}
} else if (idType.getKey() == IdType.ASSIGN_UUID.getKey()) {
metaObject.setValue(keyProperty, identifierGenerator.nextUUID(entity));
}
}
}
}
Look at the source code to know how to generate id:
identifierGenerator.nextId(entity)
So enter the method to view:
/**
* Id生成器接口
*
* @author [email protected] nieqiuqiu
* @since 2019-10-15
* @since 3.3.0
*/
public interface IdentifierGenerator {
/**
* 生成Id
*
* @param entity 实体
* @return id
*/
Number nextId(Object entity);
/**
* 生成uuid
*
* @param entity 实体
* @return uuid
*/
default String nextUUID(Object entity) {
return IdWorker.get32UUID();
}
This is getting closer and closer to the truth (excited...)
Continue to look at the implementation class of nextId() .
@Slf4j
@Component
public class CustomIdGenerator implements IdentifierGenerator {
@Override
public Number nextId(Object entity) {
return null;
}
/**
* String 类型id生成
*
* @param entity
* @return
*/
@Override
public String nextUUID(Object entity) {
return IDGenerate.randomUUID();
}
}
At this point, I am puzzled (B)???
What is the situation, the configuration class rewrites this method, and returns Null all the time. I don’t know who wrote the configuration class, it’s really a trap for me.
IdentifierGenerator identifierGenerator=new DefaultIdentifierGenerator();
System.out.println(identifierGenerator.nextId(new Object())); //单独使用雪花算法
That's all for today's article, I hope it can help you!