前言
突然被问有没有写过博客。非常惭愧,没有养成写博客的习惯,但感觉在这行里做了那么些年,有时是该记录点东西了,为了可能的遗忘,或者为网友提供那么一点点的帮助也是好的。
想起正好前两天和几个同事聊了点小问题,其实是个小技巧,就以此为题作点记录,当是练练手吧!
一、问题
现在很多系统都不允许真正删除数据库中的数据,而是通过引入删除标记字段的方式进行假删除,即我们通常说的逻辑删除。其它的数据查询都只查询标记为未删除的数据。目前流行的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,这已经是重复的值了,所以只要商品编号重复,则必然违反唯一约束!