Mybatis-Plus同時使用邏輯刪除和唯一索引的問題及解決辦法(報數(shù)據(jù)重復(fù)Duplicate entry的問題)
1 問題背景
在開發(fā)中,我們經(jīng)常會有邏輯刪除和唯一索引同時使用的情況。但當使用mybatis plus時,如果同時使用邏輯刪除和唯一索引,會報數(shù)據(jù)重復(fù)Duplicate entry的問題。
舉例來說,有表user,建立唯一索引(user_name,is_del)
CREATE TABLE `user` ( `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id', `user_name` varchar(64) DEFAULT NULL COMMENT '用戶名', `is_del` bigint(13) DEFAULT '0' COMMENT '邏輯刪除標識', PRIMARY KEY (`id`), UNIQUE KEY `unique_user_name` (`user_name`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;
如果我們插入一條數(shù)據(jù)user_name='張三’的數(shù)據(jù),然后再刪除它,這時數(shù)據(jù)表中存在一條記錄,如下圖:
這時如果再插入一條’張三’,雖然之前的一條記錄已經(jīng)被邏輯刪除,但是唯一索引只建在了user_name上,所以這時會報數(shù)據(jù)重復(fù)的錯誤
2 第一次改進
我們把唯一索引的組合增加is_del字段
UNIQUE KEY `unique_user_name_is_del` (`user_name`,`is_del`)
這下可以再次插入’張三’這條數(shù)據(jù),插入后如下圖
但是如果第二次刪除’張三’,則還是會報錯,因為已經(jīng)有一條[‘張三’,1]的數(shù)據(jù),當程序想把另一條’zhangsan’的is_del字段值為1時,會因為數(shù)據(jù)重復(fù)失?。?/p>
3 第二次改進
此時應(yīng)該如何改進呢,可以在user表中增加一個del_version字段,用來把已經(jīng)刪除的數(shù)據(jù)加上版本號,然后將這個字段也加入唯一索引中
CREATE TABLE `user` ( `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id', `user_name` varchar(64) DEFAULT NULL COMMENT '用戶名', `del_version` bigint(11) DEFAULT '0' COMMENT '版本標識,用于邏輯刪除', `is_del` bigint(13) DEFAULT '0' COMMENT '邏輯刪除標識', PRIMARY KEY (`id`), UNIQUE KEY `unique_user_name_is_del_del_version` (`user_name`,`is_del`,`del_version`) ) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;
未刪除的數(shù)據(jù)del_version=0,已刪除的數(shù)據(jù)del_version修改成這條記錄的id(自增id全局唯一),這樣既可以保證無法多次插入同名的數(shù)據(jù),又可以滿足數(shù)據(jù)可以多次刪除的情況
例如,我們兩次刪除同樣數(shù)據(jù)后,再重新插入,這時數(shù)據(jù)表中的數(shù)據(jù)如下:
4 代碼解決方案
我們使用mybatis plus提供的工具生成代碼,這時所有的service層接口都會繼承 IService 這個接口,這個接口有很多默認方法,實現(xiàn)了對數(shù)據(jù)庫的操作。
我們的思路是,新建一個IBaseService接口,繼承IService接口。在這個IBaseService接口中,重寫所有和刪除相關(guān)的方法,在其中設(shè)置【del_version】=【自增id】。而原來的所有service層接口,不再繼承IService,而是繼承我們新的IBaseService。
這樣就解決了邏輯刪除和唯一索引共用的問題,IBaseService具體代碼如下:
import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; import org.llbqhh.dao.entity.BaseDO; import java.io.Serializable; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; /** * @Author wuKeFan * @Date 2023/11/08 * @Description: 邏輯刪除前先更新版本號 */ public interface IBaseService<T extends BaseDO> extends IService<T> { /** * 根據(jù) ID 刪除 * * @param id 主鍵ID */ @Override default boolean removeById(Serializable id) { T objDO = getBaseMapper().selectById(id); return beforeDelete(objDO) && SqlHelper.retBool(getBaseMapper().deleteById(id)); } /** * 刪除對象前,先修改其版本號 * @param objDO * @return */ default boolean beforeDelete(T objDO) { if (Objects.isNull(objDO)) { return false; } // 邏輯刪除前先更新版本號 objDO.setDelVersion(objDO.getId()); return SqlHelper.retBool(getBaseMapper().updateById(objDO)); } /** * 根據(jù) columnMap 條件,刪除記錄 * * @param columnMap 表字段 map 對象 */ @Override default boolean removeByMap(Map<String, Object> columnMap) { throw new RuntimeException("不支持的數(shù)據(jù)庫刪除操作"); } /** * 根據(jù) entity 條件,刪除記錄 * * @param queryWrapper 實體包裝類 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} */ @Override default boolean remove(Wrapper<T> queryWrapper) { List<T> objDOS = getBaseMapper().selectList(queryWrapper); if (CollectionUtils.isNotEmpty(objDOS)) { objDOS.forEach(objDO -> beforeDelete(objDO)); } return SqlHelper.retBool(getBaseMapper().delete(queryWrapper)); } /** * 刪除(根據(jù)ID 批量刪除) * * @param idList 主鍵ID列表 */ @Override default boolean removeByIds(Collection<? extends Serializable> idList) { if (CollectionUtils.isEmpty(idList)) { return false; } List<T> objDOS = getBaseMapper().selectBatchIds(idList); if (CollectionUtils.isNotEmpty(objDOS)) { objDOS.forEach(objDO -> beforeDelete(objDO)); } return SqlHelper.retBool(getBaseMapper().deleteBatchIds(idList)); } }
到此這篇關(guān)于Mybatis-Plus同時使用邏輯刪除和唯一索引的問題及解決辦法的文章就介紹到這了,更多相關(guān)Mybatis-Plus邏輯刪除和唯一索引內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
struts2的國際化實現(xiàn)網(wǎng)站整體中英文切換實例代碼
本篇文章主要介紹了struts2的國際化實現(xiàn)網(wǎng)站整體中英文切換實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10Java9新特性對HTTP2協(xié)議支持與非阻塞HTTP?API
這篇文章主要為大家介紹了Java9新特性對HTTP2協(xié)議的支持與非阻塞HTTP?API,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-03-03SpringBoot集成xxl-job實現(xiàn)超牛的定時任務(wù)的步驟詳解
XXL-JOB是一個分布式任務(wù)調(diào)度平臺,其核心設(shè)計目標是開發(fā)迅速、學習簡單、輕量級、易擴展,現(xiàn)已開放源代碼并接入多家公司線上產(chǎn)品線,開箱即用,本文給大家介紹了SpringBoot集成xxl-job實現(xiàn)超牛的定時任務(wù),需要的朋友可以參考下2023-10-10詳解Java實現(xiàn)多種方式的http數(shù)據(jù)抓取
本篇文章主要介紹了Java實現(xiàn)多種方式的http數(shù)據(jù)抓取,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧。2016-12-12