项目里面用tkmapper,简化mybatis操作。
在执行updateByPrimaryKeySelective方法时,发现一直不对劲。
实体类长这样:
@Data
class Entity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Integer applyType;
private Integer status;
}
执行方法的时候这样
// 伪代码
Entity entity = new Entity();
entity.setId(1);
entity.setStatus(1);
entityMapper.updateByPrimaryKeySelective(entity);
// ...
结果更新失败了,debug的日志显示sql是这样
update entity set status = 1 where applyType = null and status = null and id = 1;
不对劲啊,where条件里面怎么这么多字段,还都是null,难道tkmapper出bug了?
翻来覆去没看到什么问题
只能去看看源码,updateByPrimaryKeySelective 长这样
@RegisterMapper
public interface UpdateByPrimaryKeySelectiveMapper<T> {
/**
* 根据主键更新属性不为null的值
*
* @param record
* @return
*/
@UpdateProvider(type = BaseUpdateProvider.class, method = "dynamicSQL")
int updateByPrimaryKeySelective(T record);
点BaseUpdateProvider看看
/**
* 通过主键更新不为null的字段
*
* @param ms
* @return
*/
public String updateByPrimaryKeySelective(MappedStatement ms) {
Class<?> entityClass = getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.updateTable(entityClass, tableName(entityClass)));
sql.append(SqlHelper.updateSetColumns(entityClass, null, true, isNotEmpty()));
sql.append(SqlHelper.wherePKColumns(entityClass, true));
return sql.toString();
}
直接看wherePKColumns怎么拼接的
public static String wherePKColumns(Class<?> entityClass, String entityName, boolean useVersion) {
StringBuilder sql = new StringBuilder();
boolean hasLogicDelete = hasLogicDeleteColumn(entityClass);
sql.append("<where>");
//获取全部列
Set<EntityColumn> columnSet = EntityHelper.getPKColumns(entityClass);
//当某个列有主键策略时,不需要考虑他的属性是否为空,因为如果为空,一定会根据主键策略给他生成一个值
for (EntityColumn column : columnSet) {
sql.append(" AND ").append(column.getColumnEqualsHolder(entityName));
}
if (useVersion) {
sql.append(whereVersion(entityClass));
}
if (hasLogicDelete) {
sql.append(whereLogicDelete(entityClass, false));
}
sql.append("</where>");
return sql.toString();
}
看到这里盲猜EntityHelper.getPKColumns(entityClass);获取的是全部字段
那PKColumns怎么获取的呢,这里要回到最前面的方法。
updateByPrimaryKeySelective 方法第一句
Class<?> entityClass = getEntityClass(ms);
public Class<?> getEntityClass(MappedStatement ms) {
String msId = ms.getId();
if (entityClassMap.containsKey(msId)) {
return entityClassMap.get(msId);
} else {
Class<?> mapperClass = getMapperClass(msId);
Type[] types = mapperClass.getGenericInterfaces();
for (Type type : types) {
if (type instanceof ParameterizedType) {
ParameterizedType t = (ParameterizedType) type;
if (t.getRawType() == this.mapperClass || this.mapperClass.isAssignableFrom((Class<?>) t.getRawType())) {
Class<?> returnType = (Class<?>) t.getActualTypeArguments()[0];
//获取该类型后,第一次对该类型进行初始化
EntityHelper.initEntityNameMap(returnType, mapperHelper.getConfig());
entityClassMap.put(msId, returnType);
return returnType;
}
}
}
}
throw new MapperException("无法获取 " + msId + " 方法的泛型信息!");
}
看对实体类初始化的方法initEntityNameMap
public static synchronized void initEntityNameMap(Class<?> entityClass, Config config) {
if (entityTableMap.get(entityClass) != null) {
return;
}
//创建并缓存EntityTable
EntityTable entityTable = resolve.resolveEntity(entityClass, config);
entityTableMap.put(entityClass, entityTable);
}
不做配置,不加注解,简单类型不会处理,源码的注释写得很清楚了
public EntityTable resolveEntity(Class<?> entityClass, Config config) {
Style style = config.getStyle();
//style,该注解优先于全局配置
if (entityClass.isAnnotationPresent(NameStyle.class)) {
NameStyle nameStyle = entityClass.getAnnotation(NameStyle.class);
style = nameStyle.value();
}
//创建并缓存EntityTable
EntityTable entityTable = null;
if (entityClass.isAnnotationPresent(Table.class)) {
Table table = entityClass.getAnnotation(Table.class);
if (!"".equals(table.name())) {
entityTable = new EntityTable(entityClass);
entityTable.setTable(table);
}
}
if (entityTable == null) {
entityTable = new EntityTable(entityClass);
//可以通过stye控制
String tableName = StringUtil.convertByStyle(entityClass.getSimpleName(), style);
//自动处理关键字
if (StringUtil.isNotEmpty(config.getWrapKeyword()) && SqlReservedWords.containsWord(tableName)) {
tableName = MessageFormat.format(config.getWrapKeyword(), tableName);
}
entityTable.setName(tableName);
}
entityTable.setEntityClassColumns(new LinkedHashSet<EntityColumn>());
entityTable.setEntityClassPKColumns(new LinkedHashSet<EntityColumn>());
//处理所有列
List<EntityField> fields = null;
if (config.isEnableMethodAnnotation()) {
fields = FieldHelper.getAll(entityClass);
} else {
fields = FieldHelper.getFields(entityClass);
}
for (EntityField field : fields) {
//如果启用了简单类型,就做简单类型校验,如果不是简单类型,直接跳过
//3.5.0 如果启用了枚举作为简单类型,就不会自动忽略枚举类型
//4.0 如果标记了 Column 或 ColumnType 注解,也不忽略
if (config.isUseSimpleType()
&& !field.isAnnotationPresent(Column.class)
&& !field.isAnnotationPresent(ColumnType.class)
&& !(SimpleTypeUtil.isSimpleType(field.getJavaType())
||
(config.isEnumAsSimpleType() && Enum.class.isAssignableFrom(field.getJavaType())))) {
continue;
}
processField(entityTable, field, config, style);
}
//当pk.size=0的时候使用所有列作为主键
if (entityTable.getEntityClassPKColumns().size() == 0) {
entityTable.setEntityClassPKColumns(entityTable.getEntityClassColumns());
}
entityTable.initPropertyMap();
return entityTable;
}
只看processField对id的处理逻辑
protected void processField(EntityTable entityTable, EntityField field, Config config, Style style) {
// 省略代码
if (field.isAnnotationPresent(Id.class)) {
entityColumn.setId(true);
}
// 省略代码
if (entityColumn.isId()) {
entityTable.getEntityClassPKColumns().add(entityColumn);
}
}
加了id注解的字段,会设置到PKColumns里面,拼sql的时候,这样where条件里面只有id了。