本文介绍springboot整合mybatis-plus在项目中使用时,写查询方法的时候,使用lambda表示查询数据,在真正执行sql的时候where条件会拼接deleted = 0 。
原因:1、全局配置配置删除字段。默认删除值=1.未删除值=0。配置方式如下:
mybatis-plus.global-config.db-config.logic-delete-field = deleted
在使用全局配置的时候,如果实体类中有deleted字段,就会拼接deleted = 0.
2. 如果全局没有配置,在实体类中单独使用代码
@Data
@Accessors(chain = true)
@TableName("order")
public class Order {
@ApiModelProperty(value = "主键ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableLogic(value = "0",delval = "1")
@ApiModelProperty(value = "是否已删除, 1:是, 0:否")
private Integer deleted;
}
这也可以生效,如果,想灵活使用deleted = 0 ,自己决定是否在查询中拼接deleted = 0。那么就不能使用全局配置,自己在查询时,如果实体类中逻辑删除字段,就会拼接,没有就不会拼接,执行删除的时候,也需要自己设置deleted 的值。
源码分析:
代码1
public class TableInfoHelper {
// 省略代码,下面这是私有方法,大家可以往上找,看各方法的功能
/**
*
* 初始化 表主键,表字段
*
*
* @param clazz 实体类
* @param globalConfig 全局配置
* @param tableInfo 数据库表反射信息
*/
private static void initTableFields(Class> clazz, GlobalConfig globalConfig, TableInfo tableInfo, List excludeProperty) {
/* 数据库全局配置 */
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
Reflector reflector = tableInfo.getReflector();
List list = getAllFields(clazz);
// 标记是否读取到主键
boolean isReadPK = false;
// 是否存在 @TableId 注解
boolean existTableId = isExistTableId(list);
// 是否存在 @TableLogic 注解
boolean existTableLogic = isExistTableLogic(list);
List fieldList = new ArrayList<>(list.size());
for (Field field : list) {
if (excludeProperty.contains(field.getName())) {
continue;
}
// 该字段是否是主键,默认FALSE,下面赋值
boolean isPK = false;
boolean isOrderBy = field.getAnnotation(OrderBy.class) != null;
/* 主键ID 初始化 */
if (existTableId) {
TableId tableId = field.getAnnotation(TableId.class);
if (tableId != null) {
if (isReadPK) {
throw ExceptionUtils.mpe("@TableId can't more than one in Class: \"%s\".", clazz.getName());
}
// 有主键,设置主键生成方法,主键名字
initTableIdWithAnnotation(dbConfig, tableInfo, field, tableId);
// 这只是主键为true
isPK = isReadPK = true;
}
} else if (!isReadPK) {
isPK = isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field);
}
if (isPK) {
if (isOrderBy) {
tableInfo.getOrderByFields().add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, true));
}
continue;
}
final TableField tableField = field.getAnnotation(TableField.class);
/* 有 @TableField 注解的字段初始化 */
if (tableField != null) {
// 注意看new TableFieldInfo()这个构造方法,执行的时候,会判断字段是否有@TableLogic注解,没有,看全局配置,
// existTableLogic这个参数是整个实体类中是否有@TableLogic注解
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, tableField, reflector, existTableLogic, isOrderBy));
continue;
}
/* 无 @TableField 注解的字段初始化 */
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, isOrderBy));
}
/* 字段列表 */
tableInfo.setFieldList(fieldList);
/* 未发现主键注解,提示警告信息 */
if (!isReadPK) {
logger.warn(String.format("Can not find table primary key in Class: \"%s\".", clazz.getName()));
}
}
}
看代码
new TableFieldInfo(dbConfig, tableInfo, field, reflector, existTableLogic, isOrderBy)
这个TableFieldInfo构造方法中,会调用this(...),在调用
this.initLogicDelete(dbConfig, field, existTableLogic);
代码2
private void initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field, boolean existTableLogic) {
/* 获取注解属性,逻辑处理字段 */
TableLogic tableLogic = field.getAnnotation(TableLogic.class);
if (null != tableLogic) {
if (StringUtils.isNotBlank(tableLogic.value())) {
this.logicNotDeleteValue = tableLogic.value();
} else {
this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
}
if (StringUtils.isNotBlank(tableLogic.delval())) {
this.logicDeleteValue = tableLogic.delval();
} else {
this.logicDeleteValue = dbConfig.getLogicDeleteValue();
}
this.logicDelete = true;
} else if (!existTableLogic) {
// 实体类中没有标记TableLogic注解,existTableLogic这个值=false,!existTableLogic=true,就会进入这个方法
String deleteField = dbConfig.getLogicDeleteField();
// 上边获取全局的删除字段名称
// 下面判断字段名称和全局定义的一样,就赋值
if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {
this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
this.logicDeleteValue = dbConfig.getLogicDeleteValue();
this.logicDelete = true;
}
}
}
在代码1方法中,tableInfo.setFieldList(fieldList);这行代码会调用tableInfo类的方法,
代码3
void setFieldList(List fieldList) {
this.fieldList = fieldList;
AtomicInteger logicDeleted = new AtomicInteger();
AtomicInteger version = new AtomicInteger();
fieldList.forEach(i -> {
// 在上边代码2中删除字段的isLogicDelete会赋值true
if (i.isLogicDelete()) {
// 这里会设置withLogicDelete=true,
this.withLogicDelete = true;
// 设置删除字段
this.logicDeleteFieldInfo = i;
logicDeleted.getAndAdd(1);
}
if (i.isWithInsertFill()) {
this.withInsertFill = true;
}
if (i.isWithUpdateFill()) {
this.withUpdateFill = true;
}
if (i.isOrderBy()) {
if (null == this.orderByFields) {
this.orderByFields = new LinkedList<>();
}
this.orderByFields.add(i);
}
if (i.isVersion()) {
this.withVersion = true;
this.versionFieldInfo = i;
version.getAndAdd(1);
}
});
/* 校验字段合法性 */
Assert.isTrue(logicDeleted.get() <= 1, "@TableLogic not support more than one in Class: \"%s\"", entityType.getName());
Assert.isTrue(version.get() <= 1, "@Version not support more than one in Class: \"%s\"", entityType.getName());
}
等调用查询方法的时候,拼接sql时会调用
public class TableInfo{
/**
* 获取逻辑删除字段的 sql 脚本
*
* @param startWithAnd 是否以 and 开头
* @param isWhere 是否需要的是逻辑删除值
* @return sql 脚本
*/
public String getLogicDeleteSql(boolean startWithAnd, boolean isWhere) {
// 如果类中有删除字段,标记了@TableLogic注解,或者字段和全局一样,withLogicDelete在代码3中已经
// 赋值=true,就会进入下面的拼接deleted = 0 方法
if (withLogicDelete) {
String logicDeleteSql = formatLogicDeleteSql(isWhere);
if (startWithAnd) {
logicDeleteSql = " AND " + logicDeleteSql;
}
return logicDeleteSql;
}
return EMPTY;
}
/**
* format logic delete SQL, can be overrided by subclass
* github #1386
*
* @param isWhere true: logicDeleteValue, false: logicNotDeleteValue
* @return sql
*/
// 真正拼接的方法
protected String formatLogicDeleteSql(boolean isWhere) {
final String value = isWhere ? logicDeleteFieldInfo.getLogicNotDeleteValue() : logicDeleteFieldInfo.getLogicDeleteValue();
if (isWhere) {
if (NULL.equalsIgnoreCase(value)) {
return logicDeleteFieldInfo.getColumn() + " IS NULL";
} else {
return logicDeleteFieldInfo.getColumn() + EQUALS + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);
}
}
final String targetStr = logicDeleteFieldInfo.getColumn() + EQUALS;
if (NULL.equalsIgnoreCase(value)) {
return targetStr + NULL;
} else {
return targetStr + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);
}
}
}
如果在查询的sql中想不拼接deleted = 0 ,解决方法:
1. 不设置全局逻辑删除字段,如果有逻辑删除,自己在代码中定义删除字段,并标记@TableLogic注解,里面可以写删除的值,和不删除的值。如果设置全局删除字段,适用于,删除场景比较少,查询比较的多的。并且项目中采用的是逻辑删除。
2.如果设置了全局的删除字段,实体类中定义的删除字段与全局的不一样。如果自己在那个字段上加@TableLogic注解,代码查询就会拼接删除=0条件,不加注解,就自己设置。
3. 如果项目查询比较多,都是采用逻辑删除,在查询的时候需要查出删除数据,就需要自己写xml的sql语句。其他lambda表达式的查询,由mybatis自动加上deleted = 0条件。