Golang gorm软删除与唯一性冲突及解决方案

371 阅读2分钟

场景描述

gorm框架中有一个Model结构体,大多数入库的结构体都会组合它

type Model struct {
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt DeletedAt `gorm:"index"`
}

gorm采取的是软删除策略,即用一个DeleteAt字段来表示是否被删除,而不去实际删除对应的记录

这样可以防止数据丢失,方便后面维护找回数据

然而这样有一个问题

假如结构体有个字段是unique唯一的,就会出现一个逻辑上的错误:明明没有同名字段的记录,却还是因为唯一性无法插入新纪录

比方说用户名是唯一的,有个名为KreanXie的老用户注销了,数据库了软删除了他的信息,此时有个新用户想叫KreanXie,却因为因为用户名唯一而注册失败


场景还原

假设有这样一个结构体

type User struct {
    gorm.Model
    Username string `gorm:"unique"`
}

他的Username字段是unique的

此时我们插入一个新的字段

image.png

然后删除它

image.png

此时如果我们再次创建一个Username相同的记录,就会报错

Error 1062 (23000): Duplicate entry 'KreanXie' for key 'users.uni_users_username'

因为Username是唯一的,所以不允许拥有相同字段的记录插入

那么应该怎么在使用软删除的同时保证字段唯一性呢?


解决方案

Gorm官方提供了一个方案,使用时间戳表示DeletedAt,同时利用组合索引保证唯一性,具体做法如下

获取需要的包

go get -u gorm.io/plugin/soft_delete

重新设计User结构体,将DeletedAt的类型改为soft_delete.DeletedAt

同时还需要把DeletedAt另一个唯一的字段,假设是Username,加上一个taggorm:"uniqueIndex:unique_idx,表示唯一索引

修改后

type User struct {
    ID        uint `gorm:"primarykey"`
    CreatedAt time.Time
    UpdatedAt time.Time
    DeletedAt soft_delete.DeletedAt `gorm:"uniqueIndex:unique_idx"`
    Username  string                `gorm:"uniqueIndex:unique_idx"`
}

同样的,我们创建一个记录然后删除它

image.png

再次创建一个同样Username的记录,成功!

image.png

再次插入一条同名记录,发现会被唯一性约束

Error 1062 (23000): Duplicate entry '0-KreanXie' for key 'users.unique_idx'

如此,我们就实现了我们的目的,在使用软删除的同时保证字段唯一性

结语

这个场景应该还是很常见的,故记之

Author:KreanXie