Entgo 实现 软删除(Soft Delete)

简介: 软删除(Soft Delete)这种删除方式并不是真正地从数据库中把记录删除,而是通过特定的标记方式在查询的时候将此记录过滤掉。虽然数据在界面上已经看不见,但是数据库还是存在的。

Entgo 实现 软删除(Soft Delete)

我们在开发程序的过程中,会遇到一个常见的需求——删除表中的数据。

但是有时候,业务需求要求不能永久删除数据库中的数据。比如一些敏感信息,我们需要留着以方便做历史追踪。
这个时候,我们便会用到软删除。

Entgo本身是不直接支持的,但是,要实现也并不是很难的事情。

什么是软删除?

软删除(Soft Delete) 是相对于 硬删除(Hard Delete) 来说的,它又可以叫做 逻辑删除 或者 标记删除

这种删除方式并不是真正地从数据库中把记录删除,而是通过特定的标记方式在查询的时候将此记录过滤掉。虽然数据在界面上已经看不见,但是数据库还是存在的。

如何实现软删除?

  1. 布尔类型字段标识
  2. 时间戳字段标识
  3. 将软删除的数据插入到另一个表中
  4. 布尔类型字段、时间戳字段混合标识

1. 布尔类型字段标识

添加一个字段名为:is_deletedis_activeis_archived等的布尔类型的字段,以此来标识该行是否已经删除。

2. 时间戳字段标识

添加一个字段名为:deleted_atdelete_time等的时间戳字段,null表示未删除,非null则表示已经删除,同时还能获取到删除的时间。

3. 将软删除的数据插入到另一个表中

举个例子,order表会有一个相应的order_deleted表,在删除order表中的数据,将数据复制到order_deleted表中。

4. 布尔类型字段、时间戳字段混合标识

使用时间戳的方式去标识,虽然可以在标识同时也可以获取到删除时间,但是在查询的时候,null值会导致查询全表扫描,导致查询的性能大打折扣。

混合布尔类型和时间戳类型的字段来进行删除标识,虽然会多占用一点存储,但是可以带来更好的费效比。

软删除使用场景

我在网上搜索到了 Abel AvramUdi Dahan 两个大佬关于要不要软删除的争论。存在的,就是有理的。软删除有其好处,也有其弊端。所以,不能够滥用,也不能完全否认它存在的意义。

在数据库的领域里面,删除只有 Delete 的概念。但是,在业务的领域里面,删除其实是有很多现实意义的概念:员工的解雇、公民的故去、订单的取消、产品的停售……

假设市场部要从商品目录中删除一样商品,那是不是说所有包含了该商品的旧订单都要一并消失?再级联下去,这些订单对应的所有发票也要删除吗?就这么一步步删下去,是不是公司的损益报表也要重做了?

软删除,它就是后悔药,可以在历史追踪,审计等场景下发挥大作用。

但是,必须要面对的是,留存大量的冗余数据,对于数据库的性能必然是不利的。

Entgo中实现软删除(Soft Deletes)

Ent框架暂时是不支持软删除的(当前版本:v0.11.4),但是实现起来也并不麻烦,代码修改量也并不大。

本着偷懒的精神,我研究了一下怎么样让代码量更少的做法,但是我并没有找寻到——这还需要框架层的支持。

创建创建删除标识字段

在Ent中创建删除字段有两种方式:

  1. 在Schema中创建删除标识(不通用);
  2. 在Mixin中创建建删除标识(通用)。

在Schema中创建删除标识

在表里面添加字段:

package schema

func (User) Fields() []ent.Field {
    return []ent.Field{
        ...
        field.Time("deleted_at").Optional().Nillable(),
        field.Bool("is_deleted").Optional().Nillable().Default(false),
        ...
    }
}

这种方式比较简单直观,但是,不够通用,需要在每一个Schema里面定义字段。

在Mixin中创建建删除标识

Mixin是Ent一个很重要也很有用的特性。我们可以把一些通用的字段提炼出来形成一个mixin包,这样这个mixin包里边的字段就可以复用了。

例如,我们创建一个SoftDelete的mixin:

package mixin

type SoftDelete struct{}

func (SoftDelete) Fields() []ent.Field {
    return []ent.Field{
        // 删除时间
        field.Time("deleted_at").
            Comment("删除时间").
            Optional().
            Nillable(),

        // 删除标识
        field.Bool("is_deleted").
            Comment("删除标识").
            Optional().
            Nillable().
            Default(false),
    }
}

然后在Schema当中引用mixin:

package schema

// Mixin of the User.
func (User) Mixin() []ent.Mixin {
    return []ent.Mixin{
        mixin.SoftDelete{},
    }
}

执行查询

查询

之前的查询操作是这样的:

    users, err := client.Debug().User.
        Query().
        Where(user.NameEQ("a8m")).
        Where(user.AgeEQ(18)).
        All(ctx)
SELECT * 
FROM users 
WHERE name = ? AND age = ?

现在变成这样:

    users, err := client.Debug().User.
        Query().
        Where(user.NameEQ("a8m")).
        Where(user.AgeEQ(18)).
        Where(user.DeletedAtIsNil()).
        Where(user.IsDeletedEQ(false)).
        All(ctx)
SELECT * 
FROM users 
WHERE (name = ? AND age = ?) AND deleted_at IS NULL AND is_deleted IS FALSe

删除

之前的删除操作是这样的:

client.Debug().User.
    DeleteOneID(1).
    Exec(ctx)
DELETE FROM users WHERE id = 1

现在变成这样:

_, err := client.Debug().User.
    UpdateOneID(id).
    SetDeletedAt(time.Now()).
    SetIsDeleted(true).
    Save(ctx)
UPDATE users 
SET deleted_at = ?, is_deleted = true
WHERE id = ?

参考资料

  1. 数据的软删除—什么时候需要?又如何去实现?
  2. Don’t Delete – Just Don’t
  3. Deleting Data Is Not a Recommended Practice
  4. Feature Request: Soft Deletes
  5. [[HELP] Trying to implement soft delete logic using Hooks and Mixins](https://github.com/ent/ent/issues/2850)
  6. To Delete or to Soft Delete, That is the Question!
目录
相关文章
|
SQL 关系型数据库 Go
Golang ORM框架介绍及比较
Golang ORM框架介绍及比较
|
消息中间件 NoSQL Kafka
订单超时取消的11种方式(非常详细清楚)
订单超时取消的11种方式(非常详细清楚)
8066 5
订单超时取消的11种方式(非常详细清楚)
|
前端开发 API UED
React 图片轮播 Carousel:从入门到进阶
本文介绍了在 React 中实现图片轮播(Carousel)的方法,从基础安装和配置 `react-slick` 开始,逐步讲解了常见问题如图片路径、性能优化、自定义样式和交互处理,以及高级话题如动态数据加载和响应式设计。通过详细示例,帮助开发者避免易错点,提升轮播图的用户体验。
295 3
|
存储 Go PHP
Go语言中的加解密利器:go-crypto库全解析
在软件开发中,数据安全和隐私保护至关重要。`go-crypto` 是一个专为 Golang 设计的加密解密工具库,支持 AES 和 RSA 等加密算法,帮助开发者轻松实现数据的加密和解密,保障数据传输和存储的安全性。本文将详细介绍 `go-crypto` 的安装、特性及应用实例。
603 0
|
Java Shell
Gradle的安装及换源
Gradle的安装及换源
7186 1
|
Kubernetes API Perl
在K8S中,如何让Pod运行一次?如何解决一次性任务?
在K8S中,如何让Pod运行一次?如何解决一次性任务?
|
人工智能 自然语言处理 算法
2024年6月上半月30篇大语言模型的论文推荐
大语言模型(LLMs)在近年来取得了快速发展。本文总结了2024年6月上半月发布的一些最重要的LLM论文,可以让你及时了解最新进展。
574 3
2024年6月上半月30篇大语言模型的论文推荐
|
算法 NoSQL 关系型数据库
九种分布式ID解决方案
在复杂的分布式系统中,往往需要对大量的数据进行唯一标识,比如在对一个订单表进行了分库分表操作,这时候数据库的自增ID显然不能作为某个订单的唯一标识。除此之外还有其他分布式场景对分布式ID的一些要求:
1348 0
|
SQL 关系型数据库 MySQL
mysql 数据库 增删改查 基本操作
mysql 数据库 增删改查 基本操作