Mybatis-Plus同时使用逻辑删除和唯一索引的问题及解决办法(报数据重复Duplicate entry的问题)

 更新时间:2023年11月08日 14:53:15   作者:吴名氏.  
在开发中,我们经常会有逻辑删除和唯一索引同时使用的情况,但当使用mybatis plus时,如果同时使用逻辑删除和唯一索引,会报数据重复Duplicate entry的问题,如何解决这个问题呢,小编给大家分享Mybatis-Plus同时使用逻辑删除和唯一索引的问题及解决办法,一起看看吧

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`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;

如果我们插入一条数据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`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;

未删除的数据del_version=0,已删除的数据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 IBaseService<T extends BaseDO> extends IService<T> {
    /**
     * 根据 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));
    }
    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    @Override
    default boolean removeByMap(Map<String, Object> columnMap) {
        throw new RuntimeException("不支持的数据库删除操作");
    }
    /**
     * 根据 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));
    }
    /**
     * 删除(根据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));
    }
}

到此这篇关于Mybatis-Plus同时使用逻辑删除和唯一索引的问题及解决办法的文章就介绍到这了,更多相关Mybatis-Plus逻辑删除和唯一索引内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring覆盖容器中Bean的注解如何实现@OverrideBean

    Spring覆盖容器中Bean的注解如何实现@OverrideBean

    文章介绍了在项目开发中如何通过偷梁换柱的方式重写Spring容器中的内置Bean,并指出了需要注意的两点:1. 对应的Bean应基于接口注入;2. 如果不是基于接口注入,可以使用同包名同类名的方式重写(可能存在潜在问题,不推荐),文章还强调了“基于接口编程”的好处
    2025-01-01
  • Java压缩和解压缩ZIP文件实战案例

    Java压缩和解压缩ZIP文件实战案例

    这篇文章主要给大家介绍了关于Java压缩和解压缩ZIP文件的相关资料,ZIP是一种较为常见的压缩形式,最近项目中遇到了再Java中压缩和解压缩zip文件的需求,特此分享给大家,需要的朋友可以参考下
    2023-07-07
  • MyBatis配置与CRUD超详细讲解

    MyBatis配置与CRUD超详细讲解

    这篇文章主要介绍了MyBatis配置与CRUD,CRUD是指在做计算处理时的增加(Create)、读取(Read)、更新(Update)和删除(Delete)几个单词的首字母简写。CRUD主要被用在描述软件系统中数据库或者持久层的基本操作功能
    2023-02-02
  • java使用OpenCV从视频文件中获取帧

    java使用OpenCV从视频文件中获取帧

    这篇文章主要为大家详细介绍了java使用OpenCV从视频文件中获取帧,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07
  • Java泛型T,E,K,V,N,?与Object区别和含义

    Java泛型T,E,K,V,N,?与Object区别和含义

    Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。本文将详细讲讲Java泛型T、E、K、V、N、?和Object区别和含义,需要发可以参考一下
    2022-03-03
  • Java实现定时任务的方法详解

    Java实现定时任务的方法详解

    大家都用过闹钟,闹钟可以说是一种定时任务。那么,在 Java 中,如何实现这样的功能呢?即如何实现定时任务呢?本文就来详细和大家聊聊
    2022-10-10
  • java如何实现postman中用x-www-form-urlencoded参数的请求

    java如何实现postman中用x-www-form-urlencoded参数的请求

    在Java开发中,模拟Postman发送x-www-form-urlencoded类型的请求是一个常见需求,本文主要介绍了如何在Java中实现这一功能,首先,需要通过导入http-client包来创建HTTP客户端,接着,利用该客户端发送Post请求
    2024-09-09
  • Java中文件管理系统FastDFS详解

    Java中文件管理系统FastDFS详解

    这篇文章主要介绍了Java中文件管理系统FastDFS详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • springboot远程执行服务器指令

    springboot远程执行服务器指令

    这篇文章主要介绍了springboot远程执行服务器指令,本例是java远程连接到服务器,去抓取查询kubesphere中的etcd日志,并返回,需要的朋友可以参考下
    2023-09-09
  • 初识Java环境变量配置及IDEA

    初识Java环境变量配置及IDEA

    这篇文章主要介绍了Java环境变量配置及IDEA,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03

最新评论