前言
Mybatisplus自动注入的方法实现了逻辑删除,就是对数据做了一个删除标识,并没有真正的删除数据。
一、TableLogic的使用
(1)、配置文件中配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
(2)注解
在字段上加上注解@TableLogic,或者按照步骤1设置字段名logic-delete-field: flag,都会被当做逻辑删除字段,优先按照注解来判断
@TableLogic
private Integer deleted;
二、TableLogic的解析
(1)在注入Mybatisplus的自定义方法时,会初实体类的字段信息
AbstractSqlInjector
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
/* 初始化字段相关 */
initTableFields(clazz, globalConfig, tableInfo, excludePropertyList);
// 是否存在 @TableLogic 注解
boolean existTableLogic = isExistTableLogic(list);
//校验@TableLogic是否存在
public static boolean isExistTableLogic(List<Field> list) {
return list.stream().anyMatch(field -> field.isAnnotationPresent(TableLogic.class));
}
(2)TableFieldInfo
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) {
String deleteField = dbConfig.getLogicDeleteField();
if (StringUtils.isNotBlank(deleteField) && this.property.equals(deleteField)) {
//不存在@TableLogic,并且当前字段与配置的逻辑删除字段一样
this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();
this.logicDeleteValue = dbConfig.getLogicDeleteValue();
this.logicDelete = true;
}
}
}
三、逻辑删除sql
(1)DeleteById ,将删除语句转换成更新语句
update user set deleted=1 where id = 1 and deleted=0
public class DeleteById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
//LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
if (tableInfo.isWithLogicDelete()) {
//获取逻辑删除sql
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),
tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
} else {
sqlMethod = SqlMethod.DELETE_BY_ID;
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
tableInfo.getKeyProperty());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
return this.addDeleteMappedStatement(mapperClass, getMethod(sqlMethod), sqlSource);
}
}
}
(2)sqlLogicSet( )
将删除修改为更新语句,获取set片段
protected String sqlLogicSet(TableInfo table) {
return "SET " + table.getLogicDeleteSql(false, false);
}
(3)getLogicDeleteSql( )
public String getLogicDeleteSql(boolean startWithAnd, boolean isWhere) {
if (withLogicDelete) {
String logicDeleteSql = formatLogicDeleteSql(isWhere);
if (startWithAnd) {
logicDeleteSql = " AND " + logicDeleteSql;
}
return logicDeleteSql;
}
return EMPTY;
}
(4)formatLogicDeleteSql( )
private String formatLogicDeleteSql(boolean isWhere) {
//根据where获取LogicNotDeleteValue或LogicDeleteValue
final String value = isWhere ? logicDeleteFieldInfo.getLogicNotDeleteValue() : logicDeleteFieldInfo.getLogicDeleteValue();
if (isWhere) {
if (NULL.equalsIgnoreCase(value)) {
//处理 where 中null值
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)) {
//处理不是where后面的语句的null值
return targetStr + NULL;
} else {
return targetStr + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);
}
}
四、逻辑查询sql
(1)SelectById ,拼接上逻辑删除字段条件
select id,name,deleted from user where deleted=0
public class SelectById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
//这里获取逻辑删除条件
tableInfo.getLogicDeleteSql(true, true)), Object.class);
return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
}
}
五、逻辑更新sql
(1)UpdateById,set片段去除逻辑删除字段,value片段
update user set name='lisi',version=1 where id=1 and version=0 and deleted=0
public class UpdateById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),
SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;
//处理乐观锁字段、逻辑删除字段
final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
//处理set,并去除逻辑删除字段
sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, ENTITY, ENTITY_DOT),
tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
}
}
总结
MybatisPlus提供了逻辑删除的功能,只能用于自动注入的方法,不能用于自定义方法;如果想让所有的sql生效,可以自定义mybatis的拦截器,在拦截器内实现修改sql的逻辑。