mybatis-plus逻辑删除功能源码解析

阅读源码 同时被 3 个专栏收录
18 篇文章 0 订阅
10 篇文章 0 订阅
13 篇文章 0 订阅

逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。

如果你需要再查出来就不应使用逻辑删除,而是以一个状态去表示。

如: 员工离职,账号被锁定等都应该是一个状态字段,此种场景不应使用逻辑删除。

 

若确需查找删除数据,如老板需要查看历史所有数据的统计汇总信息,请单独手写sql。

 

那么用户注销App是不是应该使用逻辑删除呢?因为userId在很多表中都是外键,在做OLAP的时候经常是会使用的?如果真的进行了物理删除,那么其他表是不是出现了很多脏数据?

 

DefaultSqlInjector里面已集成逻辑删除功能,entity 内字段注解 {@link TableLogic} 即可开启

效果: 使用mp自带方法删除和查找都会附带逻辑删除功能 (自己写的xml不会)

where deleted = 0

使用的时候deleted set default 0

alter table tbl_employee alter deleted set default 0;

在GlobalConfig中

private IKeyGenerator keyGenerator;

/**

 * 逻辑删除全局值(默认 1、表示已删除)

 */

private String logicDeleteValue = "1";

/**

 * 逻辑未删除全局值(默认 0、表示未删除)

 */

private String logicNotDeleteValue = "0";

 

 

为什么给这个deleted字段加上了@TableLogic字段后,就实现逻辑删除功能了呢?

其原理就是在AbstractSqlInjector获取到methodList之后,获取TableInfo的时候

@Override

public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {

    

            List<AbstractMethod> methodList = this.getMethodList();

            if (CollectionUtils.isNotEmpty(methodList)) {

                TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);

                // 循环注入自定义方法

                methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));

            

    }

}

进入到TableInfoHelper类中,调用initTableInfo方法,实体类反射获取表信息,初始化表名,表字段

public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {

    /* 数据库全局配置 */

    GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();

    List<Field> list = getAllFields(clazz);

    // 标记是否读取到主键

    boolean isReadPK = false;

    // 是否存在 @TableId 注解

    boolean existTableId = isExistTableId(list);



    List<TableFieldInfo> fieldList = new ArrayList<>();

    for (Field field : list) {

        /*

         * 主键ID 初始化

         */

        if (!isReadPK) {

            if (existTableId) {

                isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, clazz);

            } else {

                isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, clazz);

            }

            if (isReadPK) {

                continue;

            }

        }

        /*  @TableField 注解的字段初始化 */

        if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field, clazz)) {

            continue;

        }



        /*  @TableField 注解的字段初始化 */

        fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));

    }



    /* 检查逻辑删除字段只能有最多一个 */

    Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,

        String.format("annotation of @TableLogic can't more than one in class : %s.", clazz.getName()));



    /* 字段列表 */

    tableInfo.setFieldList(fieldList);



    /* 未发现主键注解,提示警告信息 */

    if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {

        logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));

    }

}

检查是否有@TableId @TableField注解,

对于@TableLogic注解的field,执行

/*  @TableField 注解的字段初始化 */

if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field, clazz)) {

    continue;

}

之后

fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, columns[i], els[i], tableField));

在TableFieldInfo的构造方法中

tableInfo.setLogicDelete(this.initLogicDelete(dbConfig, field));

处理@TableLogic字段

/**

 * 逻辑删除初始化

 *

 * @param dbConfig 数据库全局配置

 * @param field    字段属性对象

 */

private boolean initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field) {

    /* 获取注解属性,逻辑处理字段 */

    TableLogic tableLogic = field.getAnnotation(TableLogic.class);

    if (null != tableLogic) {

        if (StringUtils.isNotEmpty(tableLogic.value())) {

            this.logicNotDeleteValue = tableLogic.value();

        } else {

            this.logicNotDeleteValue = dbConfig.getLogicNotDeleteValue();

        }

        if (StringUtils.isNotEmpty(tableLogic.delval())) {

            this.logicDeleteValue = tableLogic.delval();

        } else {

            this.logicDeleteValue = dbConfig.getLogicDeleteValue();

        }

        return true;

    }

    return false;

}

 

之后在DeleteById的injectMappedStatement中

/**

 * 逻辑删除

 */

LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),

LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),

LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),

LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", "<script>\nUPDATE %s %s WHERE %s IN (%s) %s\n</script>"),

判断是否要逻辑删除,如果要,那么就选择逻辑删除对应的sql

流程图

 

  • 1
    点赞
  • 0
    评论
  • 4
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值