基於 MyBatis-Plus 解決數據庫邏輯刪除與唯一索引問題


一.問題描述

在業務中經常會有這樣一種需求即某字段不能重復,例如用戶表的手機又或者是身份證.而遇到這種問題一般兩種處理方法,一:插入或修改之前先進行一次查詢判斷是否存在該記錄;二:利用數據庫唯一索引約束保證數據的唯一性.

但如果用方法一會有兩個缺點,一是低效率,二是在高並發的系統中,很難保證其可靠性,故我們在這使用第二中方法,也就是設置唯一索引.設置唯一索引本身是沒問題的,但目前需要基於邏輯刪除之上整合.

故要思考如何才能讓兩者之間完美的整合在一起

二.解決方法

1.歷史表

每個表新建一個歷史表,存儲已經刪除的歷史數據,缺點是大量的歷史表。當然還可以參考mysql schema的table表來設計,存儲schema和tableName,然后行數據json類型存儲,需要根據場景選擇。

2.刪除時間(推薦)

刪除標志位不使用0、1,改為使用刪除時間戳來替代,使用初始值0或者Null來作為未刪除標志符,會占用一定的存儲空間,但可以顯示刪除時間,並且 MyBatisPlus 自帶就支持了這種做法,使用這個方法只需,將刪除標識字段用 datetime 存儲,邏輯未刪除值和已刪除值支持配置為字符串null,另一個值支持配置為函數來獲取值如now()
這里貼出 yml 配置文件:

mybatis-plus:
 global-config:
  db-config:
   # 設置邏輯刪除值為當前時間
   logic-delete-value: "now()" 
   # 設置未刪除值為 "null"
   logic-not-delete-value: "null" 

3.已刪除設為null(推薦)

將未刪除標識設置默認值(例如0),再將唯一字段與刪除標記添加唯一鍵約束。當某一記錄需要刪除時,將刪除標記置為NULL。
由於NULL不會和其他字段有組合唯一鍵的效果,所以當記錄被刪除時(刪除標記被置為NULL時),解除了唯一鍵的約束。

三.具體實現

這里講述的是第三種方法,也就是將已刪除設為null,這種方法呢 MyBatisPlus 本身是不支持的,故可以利用自定義BaseMapper進行拓展補充,以下是具體實現思路及代碼:

1. 繼承AbstractMethod組裝數據

LogicDeleteById.class

/**
 * 根據id邏輯刪除
 *  情況:唯一主鍵  未刪除:0  刪除:null
 *
 * @author Brave
 * @version V1.0
 * @date 2021/5/21
 */
public class LogicDeleteById extends AbstractMethod {

    /**
     * 注入自定義 MappedStatement
     *
     * @param mapperClass mapper 接口
     * @param modelClass  mapper 泛型
     * @param tableInfo   數據庫表反射信息
     * @return MappedStatement
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        // 准備語句
        String sql =  SqlMethod.UPDATE_BY_ID.getSql();
        // set語句
        String set = "set deleted_flag = null";
        // 組裝sql
        String buildSql = String.format(
                sql,
                // 表名
                tableInfo.getTableName(),
                // set值
                set,
                // 主鍵(數據庫字段名)
                tableInfo.getKeyColumn(),
                // 實體類屬性名
                tableInfo.getKeyProperty(),
                // and 主鍵 = yes | 如果是false的話就是 and 主鍵 = no
                tableInfo.getLogicDeleteSql(true, true));

        SqlSource sqlSource = languageDriver.createSqlSource(configuration, buildSql, mapperClass);
        return addUpdateMappedStatement(mapperClass, modelClass,"logicDeleteById", sqlSource);
    }

}

LogicDeleteByIds.class

/**
 * 根據ids邏輯刪除
 *  情況:唯一主鍵  未刪除:0  刪除:null
 *
 * @author Brave
 * @version V1.0
 * @date 2021/5/21
 */
public class LogicDeleteByIds extends AbstractMethod {

    /**
     * 注入自定義 MappedStatement
     *
     * @param mapperClass mapper 接口
     * @param modelClass  mapper 泛型
     * @param tableInfo   數據庫表反射信息
     * @return MappedStatement
     */
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {

        // set語句
        String set = "set deleted_flag = null";

        SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BATCH_BY_IDS;
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), set,
                tableInfo.getKeyColumn(),
                SqlScriptUtils.convertForeach("#{item}", COLLECTION, null, "item", COMMA),
                tableInfo.getLogicDeleteSql(true, true));
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);
        return addUpdateMappedStatement(mapperClass, modelClass, "logicDeleteByIds", sqlSource);
    }

}

這個類主要是將表名&字段&篩選條件&值等內容構建成SqlSource,繼而讓MyBatisPlus進行處理

2.繼承DefaultSqlInjector添加自定義方法

/**
 * 自定義 SqlInjector
 *
 * @author Brave
 * @version V1.0
 * @date 2021/5/21
 */
@Component
public class MyLogicSqlInjector extends DefaultSqlInjector {

    /**
     * 如果只需增加方法,保留MP自帶方法
     * 可以super.getMethodList() 再add
     * @return
     */
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new LogicDeleteById());
        methodList.add(new LogicDeleteByIds());
        return methodList;
    }

}

這里主要是將自定義的方法添加到通用方法的集合中

3.創建通用Mapper繼承BaseMapper

/**
 * 通用Mapper
 *
 * @author Brave
 * @version V1.0
 * @date 2021/5/21
 */
public interface SuperMapper<T> extends BaseMapper<T> {

    /**
     * 唯一主鍵情況下:根據id進行邏輯刪除
     *
     * @param id
     * @return
     */
    int logicDeleteById(Serializable id);

    /**
     * 唯一主鍵情況下:根據ids進行邏輯刪除
     *
     * @param idList
     * @return
     */
    int logicDeleteByIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
}

最后業務mapper繼承SuperMapper,即可調用自定義方法

四.結尾

自此即可實現基於 MyBatis-Plus 解決數據庫邏輯刪除與唯一索引問題,我相信這個方法不是最優的,若是老哥們有更好的 idea 歡迎留言,可以一起討論~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM