美文网首页
MBP中yml属性注入及逻辑删除过程

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

作者: CalmHeart | 来源:发表于2021-12-08 10:39 被阅读0次
    • 背景 : 参考这篇文章 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;
      }
    }
    

    相关文章

      网友评论

          本文标题:MBP中yml属性注入及逻辑删除过程

          本文链接:https://www.haomeiwen.com/subject/sjzzxrtx.html