Ent Orm 使用 SoftDelete

Ent Orm 使用 SoftDelete

 次点击
13 分钟阅读

什么是 SoftDelete

SoftDelete 软删除,即不真正删除数据,而在某行数据上增加类型 is_deleted 的删除标识,一般使用 UPDATE 语句。

启用 Interceptor

在使用 SoftDelete 之前,首先需要启用 Interceptor,即拦截器,SoftDelete 正是 Interceptor 和 Hook 的典型示例

go run -mod=mod entgo.io/ent/cmd/ent generate --template glob="./rpc/ent/template/*.tmpl" ./rpc/ent/schema --feature sql/execquery,intercept

或者在 Go-Zero 的 Makefile 做如下修改(仅在使用 Goctls 的情况下支持)

# Ent enabled features | Ent 启用的官方特性
ENT_FEATURE=sql/execquery,intercept

添加之后执行 make gen-ent , 生成 Interceptor.

添加 Mixin

Ent 提供了官方的 Mixin,只需要引入即可。

// SoftDeleteMixin implements the soft delete pattern for schemas.
type SoftDeleteMixin struct {
    mixin.Schema
}

// Fields of the SoftDeleteMixin.
func (SoftDeleteMixin) Fields() []ent.Field {
    return []ent.Field{
        field.Time("delete_time").
            Optional(),
    }
}

type softDeleteKey struct{}

// SkipSoftDelete returns a new context that skips the soft-delete interceptor/mutators.
func SkipSoftDelete(parent context.Context) context.Context {
    return context.WithValue(parent, softDeleteKey{}, true)
}

// Interceptors of the SoftDeleteMixin.
func (d SoftDeleteMixin) Interceptors() []ent.Interceptor {
    return []ent.Interceptor{
        intercept.TraverseFunc(func(ctx context.Context, q intercept.Query) error {
            // Skip soft-delete, means include soft-deleted entities.
            if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
                return nil
            }
            d.P(q)
            return nil
        }),
    }
}

// Hooks of the SoftDeleteMixin.
func (d SoftDeleteMixin) Hooks() []ent.Hook {
    return []ent.Hook{
        hook.On(
            func(next ent.Mutator) ent.Mutator {
                return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
                    // Skip soft-delete, means delete the entity permanently.
                    if skip, _ := ctx.Value(softDeleteKey{}).(bool); skip {
                        return next.Mutate(ctx, m)
                    }
                    mx, ok := m.(interface {
                        SetOp(ent.Op)
                        Client() *gen.Client
                        SetDeleteTime(time.Time)
                        WhereP(...func(*sql.Selector))
                    })
                    if !ok {
                        return nil, fmt.Errorf("unexpected mutation type %T", m)
                    }
                    d.P(mx)
                    mx.SetOp(ent.OpUpdate)
                    mx.SetDeleteTime(time.Now())
                    return mx.Client().Mutate(ctx, m)
                })
            },
            ent.OpDeleteOne|ent.OpDelete,
        ),
    }
}

// P adds a storage-level predicate to the queries and mutations.
func (d SoftDeleteMixin) P(w interface{ WhereP(...func(*sql.Selector)) }) {
    w.WhereP(
        sql.FieldIsNull(d.Fields()[0].Descriptor().Name),
    )
}

如果使用为 Goctls 可以通过执行命令 goctls extra ent mixin -a soft_delete 来添加 Mixin

修改 Service Context

service_context.go 中添加本地的 ent/runtime

路径: internal/svc/service_context.go

引用 Mixin

在需要添加软删除的 schema 引用 mixins

// 例子
func (Task) Mixin() []ent.Mixin {
	return []ent.Mixin{
		mixins.IDMixin{},
		mixins.StatusMixin{},
		mixins2.SoftDeleteMixin{},
	}
}

最后更新 Ent 代码生成即可

注意事项

由于添加软删除后会添加 deleted_at 字段,所以需要重新初始化数据库,

© 本文著作权归作者所有,未经许可不得转载使用。