网站建设进度计划,wordpress 手机 判断,企业网站的设计要点,做网站的最大的挑战是什么1 问题背景
在开发中#xff0c;我们经常会有逻辑删除和唯一索引同时使用的情况。但当使用mybatis plus时#xff0c;如果同时使用逻辑删除和唯一索引#xff0c;会报数据重复Duplicate entry的问题。
举例来说#xff0c;有表user#xff0c;建立唯一索引#xff08;u…1 问题背景
在开发中我们经常会有逻辑删除和唯一索引同时使用的情况。但当使用mybatis plus时如果同时使用逻辑删除和唯一索引会报数据重复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)
) ENGINEInnoDB AUTO_INCREMENT11 DEFAULT CHARSETutf8mb4;如果我们插入一条数据user_name张三’的数据然后再删除它这时数据表中存在一条记录如下图 这时如果再插入一条’张三’虽然之前的一条记录已经被逻辑删除但是唯一索引只建在了user_name上所以这时会报数据重复的错误
2 第一次改进
我们把唯一索引的组合增加is_del字段
UNIQUE KEY unique_user_name_is_del (user_name,is_del)这下可以再次插入’张三’这条数据插入后如下图 但是如果第二次删除’张三’则还是会报错因为已经有一条[‘张三’,1]的数据当程序想把另一条’zhangsan’的is_del字段值为1时会因为数据重复失败
3 第二次改进
此时应该如何改进呢可以在user表中增加一个del_version字段用来把已经删除的数据加上版本号然后将这个字段也加入唯一索引中
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)
) ENGINEInnoDB AUTO_INCREMENT11 DEFAULT CHARSETutf8mb4;未删除的数据del_version0已删除的数据del_version修改成这条记录的id自增id全局唯一这样既可以保证无法多次插入同名的数据又可以满足数据可以多次删除的情况 例如我们两次删除同样数据后再重新插入这时数据表中的数据如下 4 代码解决方案
我们使用mybatis plus提供的工具生成代码这时所有的service层接口都会继承 IService 这个接口这个接口有很多默认方法实现了对数据库的操作。
我们的思路是新建一个IBaseService接口继承IService接口。在这个IBaseService接口中重写所有和删除相关的方法在其中设置【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 IBaseServiceT extends BaseDO extends IServiceT {/*** 根据 ID 删除** param id 主键ID*/Overridedefault 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));}/*** 根据 columnMap 条件删除记录** param columnMap 表字段 map 对象*/Overridedefault boolean removeByMap(MapString, Object columnMap) {throw new RuntimeException(不支持的数据库删除操作);}/*** 根据 entity 条件删除记录** param queryWrapper 实体包装类 {link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}*/Overridedefault boolean remove(WrapperT queryWrapper) {ListT objDOS getBaseMapper().selectList(queryWrapper);if (CollectionUtils.isNotEmpty(objDOS)) {objDOS.forEach(objDO - beforeDelete(objDO));}return SqlHelper.retBool(getBaseMapper().delete(queryWrapper));}/*** 删除根据ID 批量删除** param idList 主键ID列表*/Overridedefault boolean removeByIds(Collection? extends Serializable idList) {if (CollectionUtils.isEmpty(idList)) {return false;}ListT objDOS getBaseMapper().selectBatchIds(idList);if (CollectionUtils.isNotEmpty(objDOS)) {objDOS.forEach(objDO - beforeDelete(objDO));}return SqlHelper.retBool(getBaseMapper().deleteBatchIds(idList));}
}