- 背景 : 参考这篇文章 https://www.cnblogs.com/54chensongxia/p/14247966.html 想做唯一索引并且使用MybatisPlus做逻辑删除保证数据正常插入
想采用的是下面一个解决方案
在实现的过程碰到的问题 做一下汇总 logic-delete-value: null - 当前使用的mbp版本是3.3.1
- yml
# mybatis-plus
#具体默认配置项参考com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties
mybatis-plus:
#参考默认配置项 可以忽略
mapper-locations: classpath*:/mapper/**/*.xml
#具体默认配置项参考com.baomidou.mybatisplus.core.config.GlobalConfig
global-config:
#具体默认配置项参考com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig
db-config:
# 默认配置是1 需要使用null覆盖
logic-delete-value: null
# 默认配置是0 可以忽略
logic-not-delete-value: 0
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 注入
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean
- 注入MybatisPlusProperties
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS) 注意这个前缀
public class MybatisPlusProperties
由此可见yml中的mapperLocations冗余
- 逻辑删除:
-
3.1 com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject
-
3.2 com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo
-
3.3 com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableFields
-
3.4 com.baomidou.mybatisplus.core.metadata.TableFieldInfo#TableFieldInfo(com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig, com.baomidou.mybatisplus.core.metadata.TableInfo, java.lang.reflect.Field)
-
3.5 com.baomidou.mybatisplus.core.metadata.TableFieldInfo#initLogicDelete
-
3.6 上述3.3步骤中还有一个核心方法
com.baomidou.mybatisplus.core.metadata.TableInfo#setFieldList
void setFieldList(List<TableFieldInfo> fieldList) {
this.fieldList = fieldList;
fieldList.forEach(i -> {
if (i.isLogicDelete()) {//核心方法
this.logicDelete = true;
}
if (i.isWithInsertFill()) {
this.withInsertFill = true;
}
if (i.isWithUpdateFill()) {
this.withUpdateFill = true;
}
if (i.isVersion()) {
this.withVersion = true;
this.versionFieldInfo = i;
}
});
}
- 3.7 com.baomidou.mybatisplus.core.metadata.TableFieldInfo#isLogicDelete
/**
* 是否启用了逻辑删除
*/
public boolean isLogicDelete() {
return StringUtils.isNotBlank(logicDeleteValue) && StringUtils.isNotBlank(logicNotDeleteValue);
}
- 如果mybatis-plus.global-config.db-config.logic-delete-value:null 那么springboot注入得到的值是"" 需要研究下
参考SpringBoot yml配置null坑
所以这里判断不是逻辑删除 所以后续走了物理删除
- 解决方案1:
# mybatis-plus
#具体默认配置项参考com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties
mybatis-plus:
#参考默认配置项 可以忽略
mapper-locations: classpath*:/mapper/**/*.xml
#具体默认配置项参考com.baomidou.mybatisplus.core.config.GlobalConfig
global-config:
#具体默认配置项参考com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig
db-config:
# 默认配置是1 需要使用null覆盖
logic-delete-value: "null"
# 默认配置是0 可以忽略
logic-not-delete-value: 0
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 这样可以解决的原因需要参考mbp底层对于"null"的处理逻辑,代码片段如下:
com.baomidou.mybatisplus.core.metadata.TableInfo#formatLogicDeleteSql
/**
* format logic delete SQL, can be overrided by subclass
* github #1386
*
* @param isWhere true: logicDeleteValue, false: logicNotDeleteValue
* @return sql
*/
private 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);
}
}
tips: String NULL = "null";
- 解决方案2:
拦截逻辑删除处理的语句:方式是重写SystemLogicDeleteById覆盖默认的LogicDeleteSqlInjector
/**
* 根据 id 逻辑删除数据,并带字段填充功能
* <p>注意入参是 entity !!! ,如果字段没有自动填充,就只是单纯的逻辑删除</p>
* <p>
* 自己的通用 mapper 如下使用:
* <pre>
* int deleteByIdWithFill(T entity);
* </pre>
* </p>
*
* @author miemie
* @since 2018-11-09
*/
public class SystemLogicDeleteById extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;
if (tableInfo.isLogicDelete()) {
List<TableFieldInfo> fieldInfos = tableInfo.getFieldList().stream()
.filter(TableFieldInfo::isWithUpdateFill)
.collect(toList());
if (CollectionUtils.isNotEmpty(fieldInfos)) {
String sqlSet = "SET " + fieldInfos.stream().map(i -> i.getSqlSet(EMPTY)).collect(joining(EMPTY))
+ tableInfo.getLogicDeleteSql(false, false);
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlSet, tableInfo.getKeyColumn(),
tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true));
} else {
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), "SET deleted=null",
tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true));
}
} else {
sqlMethod = SqlMethod.DELETE_BY_ID;
sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),
tableInfo.getKeyProperty());
}
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);
}
@Override
public String getMethod(SqlMethod sqlMethod) {
// 自定义 mapper 方法名
return "deleteByIdWithFill";
}
}
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = {"com.compass.msg.**.mapper"})
public class MybatisPlusConfig {
/**
* mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
//开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return new PaginationInterceptor();
}
/**
* 乐观锁mybatis插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
/**
* 逻辑删除sql注射器
*
* @return {@link LogicDeleteSqlInjector }
* @author lvsheng
* @date 2021/11/22 12:48
*/
@Bean
public LogicDeleteSqlInjector logicDeleteSqlInjector() {
return new LogicDeleteSqlInjector();
}
/**
* 逻辑删除sql注射器
*
* @author lvsheng
* @version 1.0.0
* @date 2021/11/22 12:50
* @see DefaultSqlInjector
*/
public class LogicDeleteSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new SystemLogicDeleteById());
return methodList;
}
}