一、问题
现在很多系统都不允许真正删除数据库中的数据,而是通过引入删除标记字段的方式进行假删除,即我们通常说的逻辑删除。其它的数据查询都只查询标记为未删除的数据。目前流行的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)is_deleted 为0代表非删除,为主键id时代表删除
2)根据unqiue字段+is_deleted字段设置联合唯一约束。这样可以满足:当unique字段存在且未被删除时,可以标识其唯一性。而当被删除时,不会产生唯一冲突。总之,这样做保证了没删除时只能有一个存在,而删除时可以允许多个存在。