MBP中yml属性注入及逻辑删除过程

  • 背景 : 参考这篇文章 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
    1. 注入
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisPlusProperties.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean
  1. 注入MybatisPlusProperties
@Data
@Accessors(chain = true)
@ConfigurationProperties(prefix = Constants.MYBATIS_PLUS) 注意这个前缀
public class MybatisPlusProperties
部分默认值

由此可见yml中的mapperLocations冗余


嵌套注入配置类
  1. 逻辑删除:
  • 3.1 com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject


    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坑
    所以这里判断不是逻辑删除 所以后续走了物理删除
LogicDeleteByIdWithFill

debug截图

debug stack
  • 解决方案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;
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容