逻辑删除与唯一约束冲突问题的一个解决方案

前言

突然被问有没有写过博客。非常惭愧,没有养成写博客的习惯,但感觉在这行里做了那么些年,有时是该记录点东西了,为了可能的遗忘,或者为网友提供那么一点点的帮助也是好的。

想起正好前两天和几个同事聊了点小问题,其实是个小技巧,就以此为题作点记录,当是练练手吧!

一、问题

现在很多系统都不允许真正删除数据库中的数据,而是通过引入删除标记字段的方式进行假删除,即我们通常说的逻辑删除。其它的数据查询都只查询标记为未删除的数据。目前流行的hibernate 和 mybatis 等框架也提供了对假删除的支持。据我的一些同行说,他们公司甚至不允许在程序中出现删除语句,甚至在实施时,分配给你的数据库用户就不具有删除数据的权限!

但是逻辑删除会面临一个违反唯一约束的问题。比如一个商品表:

create table goods (

    id bigint primary key,

    goods_no varchar(20) not null comment '商品编号',

    goods_name varchar(20) comment '商品名称',

    ......

    deleted bit comment '是否删除', 

    constraint uq_goods_goods_no unique(goods_no)

) ;

当用户添加了一个编码为 '001' 的商品,并且删除之后,goods 表中便有一条goods_no 字段为 '001' 的记录。而如果用户想再添加一个编号为 '001' 的商品,则无法再添加进去了,因为这条数据已经在表中存在了。

但从用户角度看,编号为‘001’ 的这个商品明明都已经删除了;但再次添加的时候,却又说编号已经被占用;好吧,既然已经存在,那我去把它找出来进行修改,或者再删除之后重新添加,却又找不到这条记录!真是莫名其妙!

二、解决

要想解决这种问题,最简单的办法就是在数据库中不要创建唯一约束,数据的唯一性靠应用程序来解决。但可能某种性格的设计师就不乐意了!

笔者认为,可能为了系统运行的性能之类的原因,数据库中可以不用创建各种约束。但在开发阶梯,应当尽量多地在数据为吕创建一些约束。因为约束体现了业务规则,只要违反了约束,肯定会违反业务规则,这样能够把开发过程中可能产生的 bug 尽早地暴露出来,尽早地修复。这样比将来发现问题,再去修改程序,还要修复错误的数据要好得多。其实要坚持在数据库中创建唯一约束,办法也很简单:

1)将删除标记字段类型改为与主键相同的类型,当值为0(主键是数值类型时)或者为空/空字符串(主键是字符串类型)时,表示未删除,当想标记数据为删除状态时,将删除标记字段值修改为与主键相同的值

2)字段的唯一约束,改为字段.与删除标记字段的联合唯一约束

以上述商品表为例,商品表定义改为:

create table goods (

    id bigint primary key,

    goods_no varchar(20) not null comment '商品编号',

    goods_name varchar(20) comment '商品名称',

    ......

    deleted bigint default 0 not null comment '是否删除,当值为0时,表示未删除,当值为非0时,表示已经删除', 

    constraint uq_goods_goods_no unique(goods_no, deleted)

) ;

添加了一个商品,数据库中存在这样一条记录:

{

    "id": 10,

    "goods_no": "001",

    "goods_name": "XXXX牌XXX包装的洗衣粉", 

    "deleted": 0

)

当删除这条记录时,这条记录变成了:

{

    "id": 10,

    "goods_no": "001",

    "goods_name": "XXXX牌XXX包装的洗衣粉", 

    "deleted": 10

)

注意删除字段 deleted = 10,这个字段值取自本条记录的 id 值 

当要再次添加编号为 '001' 的商品时,可以是:

{

    "id": 11,

    "goods_no": "001",

    "goods_name": "XXX牌洋娃娃", 

    "deleted": 0

)

虽然商品编号为 001 的记录已经存在,但这个唯一约束是 goods_no 和 deleted 在一起的,所以不会违反唯一约束。

而如果新加的这条编号为'001'的商品未删除时,想继续添加一条编号为 '001' 的商品,由于新加的记录 deleted = 0,因此会尝试生成两条  goods_no = '001', deleted = 0 的记录,这时违反了唯一约束,添加失败。

var code = "3a62c96a-8d0e-4d5c-a49f-4e7a4de9bdfb"

由此,即满足了逻辑删除的需求,同时又可以不必违反唯一约束。

简单总结一下即是:只要是被标记删除的数据,虽然商品编号可能存在重复,但删除标记字段的值不可能重复(它的值即是主键的值,而主键本身不可能重复);而被标记未被删除的数据,由于其删除标记值都为0,这已经是重复的值了,所以只要商品编号重复,则必然违反唯一约束!

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值