From 1b27c50a90c246d0a504db3709c9d0f3a0721d38 Mon Sep 17 00:00:00 2001 From: hupeh Date: Wed, 25 Oct 2023 11:39:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(db):=20=E5=B0=86=20Expr=20=E4=B8=8A?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E6=B3=95=E6=89=A9=E5=B1=95=E5=88=B0=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E7=9A=84=E6=9E=84=E9=80=A0=E5=99=A8=E4=B8=8A=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/db/delete_builder.go | 104 ++++++++++++++++++++++++++-- pkg/db/expr.go | 4 ++ pkg/db/query_builder.go | 96 +++++++++++++++++++++++++- pkg/db/repository.go | 16 ++++- pkg/db/update_builder.go | 144 +++++++++++++++++++++++++++++++-------- 5 files changed, 327 insertions(+), 37 deletions(-) diff --git a/pkg/db/delete_builder.go b/pkg/db/delete_builder.go index 881dd8e..769d01b 100644 --- a/pkg/db/delete_builder.go +++ b/pkg/db/delete_builder.go @@ -1,18 +1,110 @@ package db -import "gorm.io/gorm" +import ( + "gorm.io/gorm" +) type DeleteBuilder[T any] struct { - Expr - db *gorm.DB + db *gorm.DB + expr *Expr } func NewDeleteBuilder[T any](db *gorm.DB) *DeleteBuilder[T] { - return &DeleteBuilder[T]{Expr{}, db} + return &DeleteBuilder[T]{db: db, expr: &Expr{}} } -func (b *DeleteBuilder[T]) Commit() (int64, error) { +func (d *DeleteBuilder[T]) Eq(col string, val any) *DeleteBuilder[T] { + d.expr.Eq(col, val) + return d +} + +func (d *DeleteBuilder[T]) Neq(col string, val any) *DeleteBuilder[T] { + d.expr.Neq(col, val) + return d +} + +func (d *DeleteBuilder[T]) Lt(col string, val any) *DeleteBuilder[T] { + d.expr.Lt(col, val) + return d +} + +func (d *DeleteBuilder[T]) Lte(col string, val any) *DeleteBuilder[T] { + d.expr.Lte(col, val) + return d +} + +func (d *DeleteBuilder[T]) Gt(col string, val any) *DeleteBuilder[T] { + d.expr.Gt(col, val) + return d +} + +func (d *DeleteBuilder[T]) Gte(col string, val any) *DeleteBuilder[T] { + d.expr.Gte(col, val) + return d +} + +func (d *DeleteBuilder[T]) Between(col string, less, more any) *DeleteBuilder[T] { + d.expr.Between(col, less, more) + return d +} + +func (d *DeleteBuilder[T]) NotBetween(col string, less, more any) *DeleteBuilder[T] { + d.expr.NotBetween(col, less, more) + return d +} + +func (d *DeleteBuilder[T]) IsNull(col string) *DeleteBuilder[T] { + d.expr.IsNull(col) + return d +} + +func (d *DeleteBuilder[T]) NotNull(col string) *DeleteBuilder[T] { + d.expr.NotNull(col) + return d +} + +func (d *DeleteBuilder[T]) Like(col, tpl string) *DeleteBuilder[T] { + d.expr.Like(col, tpl) + return d +} + +func (d *DeleteBuilder[T]) NotLike(col, tpl string) *DeleteBuilder[T] { + d.expr.NotLike(col, tpl) + return d +} + +func (d *DeleteBuilder[T]) In(col string, values ...any) *DeleteBuilder[T] { + d.expr.In(col, values...) + return d +} + +func (d *DeleteBuilder[T]) NotIn(col string, values ...any) *DeleteBuilder[T] { + d.expr.NotIn(col, values...) + return d +} + +func (d *DeleteBuilder[T]) When(condition bool, then func(ex *Expr), elses ...func(ex *Expr)) *DeleteBuilder[T] { + d.expr.When(condition, then, elses...) + return d +} + +func (d *DeleteBuilder[T]) Or(or func(ex *Expr)) *DeleteBuilder[T] { + d.expr.Or(or) + return d +} + +func (d *DeleteBuilder[T]) And(and func(ex *Expr)) *DeleteBuilder[T] { + d.expr.And(and) + return d +} + +func (d *DeleteBuilder[T]) Not(not func(ex *Expr)) *DeleteBuilder[T] { + d.expr.Not(not) + return d +} + +func (d *DeleteBuilder[T]) Commit() (int64, error) { var t T - res := b.db.Scopes(b.Scopes).Delete(&t) + res := d.db.Scopes(d.expr.Scopes).Delete(&t) return res.RowsAffected, res.Error } diff --git a/pkg/db/expr.go b/pkg/db/expr.go index c886bf8..a37c057 100644 --- a/pkg/db/expr.go +++ b/pkg/db/expr.go @@ -9,6 +9,10 @@ type Expr struct { clauses []clause.Expression } +func NewExpr() *Expr { + return &Expr{} +} + func (e *Expr) add(expr clause.Expression) *Expr { e.clauses = append(e.clauses, expr) return e diff --git a/pkg/db/query_builder.go b/pkg/db/query_builder.go index 4f29a3a..0bb2bcb 100644 --- a/pkg/db/query_builder.go +++ b/pkg/db/query_builder.go @@ -9,10 +9,10 @@ import ( // QueryBuilder 查询构造器 // TODO(hupeh):实现 joins 和表别名 type QueryBuilder[T any] struct { - Expr db *gorm.DB selects []string omits []string + expr *Expr orders []string limit int offset int @@ -21,7 +21,7 @@ type QueryBuilder[T any] struct { } func NewQueryBuilder[T any](db *gorm.DB) *QueryBuilder[T] { - return &QueryBuilder[T]{Expr: Expr{}, db: db} + return &QueryBuilder[T]{expr: &Expr{}, db: db} } type preload struct { @@ -46,6 +46,96 @@ func (q *QueryBuilder[T]) Omit(columns ...string) *QueryBuilder[T] { return q } +func (q *QueryBuilder[T]) Eq(col string, val any) *QueryBuilder[T] { + q.expr.Eq(col, val) + return q +} + +func (q *QueryBuilder[T]) Neq(col string, val any) *QueryBuilder[T] { + q.expr.Neq(col, val) + return q +} + +func (q *QueryBuilder[T]) Lt(col string, val any) *QueryBuilder[T] { + q.expr.Lt(col, val) + return q +} + +func (q *QueryBuilder[T]) Lte(col string, val any) *QueryBuilder[T] { + q.expr.Lte(col, val) + return q +} + +func (q *QueryBuilder[T]) Gt(col string, val any) *QueryBuilder[T] { + q.expr.Gt(col, val) + return q +} + +func (q *QueryBuilder[T]) Gte(col string, val any) *QueryBuilder[T] { + q.expr.Gte(col, val) + return q +} + +func (q *QueryBuilder[T]) Between(col string, less, more any) *QueryBuilder[T] { + q.expr.Between(col, less, more) + return q +} + +func (q *QueryBuilder[T]) NotBetween(col string, less, more any) *QueryBuilder[T] { + q.expr.NotBetween(col, less, more) + return q +} + +func (q *QueryBuilder[T]) IsNull(col string) *QueryBuilder[T] { + q.expr.IsNull(col) + return q +} + +func (q *QueryBuilder[T]) NotNull(col string) *QueryBuilder[T] { + q.expr.NotNull(col) + return q +} + +func (q *QueryBuilder[T]) Like(col, tpl string) *QueryBuilder[T] { + q.expr.Like(col, tpl) + return q +} + +func (q *QueryBuilder[T]) NotLike(col, tpl string) *QueryBuilder[T] { + q.expr.NotLike(col, tpl) + return q +} + +func (q *QueryBuilder[T]) In(col string, values ...any) *QueryBuilder[T] { + q.expr.In(col, values...) + return q +} + +func (q *QueryBuilder[T]) NotIn(col string, values ...any) *QueryBuilder[T] { + q.expr.NotIn(col, values...) + return q +} + +func (q *QueryBuilder[T]) When(condition bool, then func(ex *Expr), elses ...func(ex *Expr)) *QueryBuilder[T] { + q.expr.When(condition, then, elses...) + return q +} + +func (q *QueryBuilder[T]) Or(or func(ex *Expr)) *QueryBuilder[T] { + q.expr.Or(or) + return q +} + +func (q *QueryBuilder[T]) And(and func(ex *Expr)) *QueryBuilder[T] { + q.expr.And(and) + return q +} + +func (q *QueryBuilder[T]) Not(not func(ex *Expr)) *QueryBuilder[T] { + q.expr.Not(not) + return q +} + func (q *QueryBuilder[T]) DescentBy(columns ...string) *QueryBuilder[T] { for _, col := range columns { q.orders = append(q.orders, col+" DESC") @@ -113,7 +203,7 @@ func (q *QueryBuilder[T]) scopesWithoutEffect(tx *gorm.DB) *gorm.DB { if len(q.distinct) > 0 { tx = tx.Distinct(q.distinct...) } - return q.Expr.Scopes(tx) + return q.expr.Scopes(tx) } func (q *QueryBuilder[T]) Count() (int64, error) { diff --git a/pkg/db/repository.go b/pkg/db/repository.go index bff39f1..0c92bc7 100644 --- a/pkg/db/repository.go +++ b/pkg/db/repository.go @@ -70,6 +70,20 @@ func (r *Repository[T]) GetByID(ctx context.Context, id any) (*T, error) { return &entity, nil } +func (r *Repository[T]) GetBy(ctx context.Context, expr ...*Expr) (*T, error) { + var entity T + err := r.DB(ctx).Model(&entity).Scopes(func(tx *gorm.DB) *gorm.DB { + for _, e := range expr { + tx = e.Scopes(tx) + } + return tx + }).First(&entity).Error + if err != nil { + return nil, err + } + return &entity, nil +} + func (r *Repository[T]) Find(ctx context.Context, expr ...*Expr) ([]*T, error) { var entity T var items []*T @@ -88,7 +102,7 @@ func (r *Repository[T]) Find(ctx context.Context, expr ...*Expr) ([]*T, error) { func (r *Repository[T]) Paginate(ctx context.Context, expr ...*Expr) (*Pager[T], error) { qb := NewQueryBuilder[T](r.DB(ctx)) for _, e := range expr { - qb.Expr = *e + qb.expr = e } return qb.Paginate() } diff --git a/pkg/db/update_builder.go b/pkg/db/update_builder.go index 9d12364..dbb999f 100644 --- a/pkg/db/update_builder.go +++ b/pkg/db/update_builder.go @@ -7,55 +7,145 @@ import ( ) type UpdateBuilder[T any] struct { - Expr db *gorm.DB selects []string omits []string onConflict *clause.OnConflict + expr *Expr } func NewUpdateBuilder[T any](db *gorm.DB) *UpdateBuilder[T] { - return &UpdateBuilder[T]{Expr: Expr{}, db: db} + return &UpdateBuilder[T]{expr: &Expr{}, db: db} } -func (b *UpdateBuilder[T]) Select(columns ...string) *UpdateBuilder[T] { - b.selects = append(b.selects, columns...) - return b +func (u *UpdateBuilder[T]) Select(columns ...string) *UpdateBuilder[T] { + u.selects = append(u.selects, columns...) + return u } -func (b *UpdateBuilder[T]) Omit(columns ...string) *UpdateBuilder[T] { - b.omits = append(b.omits, columns...) - return b +func (u *UpdateBuilder[T]) Omit(columns ...string) *UpdateBuilder[T] { + u.omits = append(u.omits, columns...) + return u } -func (b *UpdateBuilder[T]) OnConflict(conflict clause.OnConflict) *UpdateBuilder[T] { - if b.onConflict == nil { - b.onConflict = &conflict +func (u *UpdateBuilder[T]) Eq(col string, val any) *UpdateBuilder[T] { + u.expr.Eq(col, val) + return u +} + +func (u *UpdateBuilder[T]) Neq(col string, val any) *UpdateBuilder[T] { + u.expr.Neq(col, val) + return u +} + +func (u *UpdateBuilder[T]) Lt(col string, val any) *UpdateBuilder[T] { + u.expr.Lt(col, val) + return u +} + +func (u *UpdateBuilder[T]) Lte(col string, val any) *UpdateBuilder[T] { + u.expr.Lte(col, val) + return u +} + +func (u *UpdateBuilder[T]) Gt(col string, val any) *UpdateBuilder[T] { + u.expr.Gt(col, val) + return u +} + +func (u *UpdateBuilder[T]) Gte(col string, val any) *UpdateBuilder[T] { + u.expr.Gte(col, val) + return u +} + +func (u *UpdateBuilder[T]) Between(col string, less, more any) *UpdateBuilder[T] { + u.expr.Between(col, less, more) + return u +} + +func (u *UpdateBuilder[T]) NotBetween(col string, less, more any) *UpdateBuilder[T] { + u.expr.NotBetween(col, less, more) + return u +} + +func (u *UpdateBuilder[T]) IsNull(col string) *UpdateBuilder[T] { + u.expr.IsNull(col) + return u +} + +func (u *UpdateBuilder[T]) NotNull(col string) *UpdateBuilder[T] { + u.expr.NotNull(col) + return u +} + +func (u *UpdateBuilder[T]) Like(col, tpl string) *UpdateBuilder[T] { + u.expr.Like(col, tpl) + return u +} + +func (u *UpdateBuilder[T]) NotLike(col, tpl string) *UpdateBuilder[T] { + u.expr.NotLike(col, tpl) + return u +} + +func (u *UpdateBuilder[T]) In(col string, values ...any) *UpdateBuilder[T] { + u.expr.In(col, values...) + return u +} + +func (u *UpdateBuilder[T]) NotIn(col string, values ...any) *UpdateBuilder[T] { + u.expr.NotIn(col, values...) + return u +} + +func (u *UpdateBuilder[T]) When(condition bool, then func(ex *Expr), elses ...func(ex *Expr)) *UpdateBuilder[T] { + u.expr.When(condition, then, elses...) + return u +} + +func (u *UpdateBuilder[T]) Or(or func(ex *Expr)) *UpdateBuilder[T] { + u.expr.Or(or) + return u +} + +func (u *UpdateBuilder[T]) And(and func(ex *Expr)) *UpdateBuilder[T] { + u.expr.And(and) + return u +} + +func (u *UpdateBuilder[T]) Not(not func(ex *Expr)) *UpdateBuilder[T] { + u.expr.Not(not) + return u +} + +func (u *UpdateBuilder[T]) OnConflict(conflict clause.OnConflict) *UpdateBuilder[T] { + if u.onConflict == nil { + u.onConflict = &conflict } else { - b.onConflict.Columns = conflict.Columns - b.onConflict.Where = conflict.Where - b.onConflict.TargetWhere = conflict.TargetWhere - b.onConflict.OnConstraint = conflict.OnConstraint - b.onConflict.DoNothing = conflict.DoNothing - b.onConflict.DoUpdates = conflict.DoUpdates - b.onConflict.UpdateAll = conflict.UpdateAll + u.onConflict.Columns = conflict.Columns + u.onConflict.Where = conflict.Where + u.onConflict.TargetWhere = conflict.TargetWhere + u.onConflict.OnConstraint = conflict.OnConstraint + u.onConflict.DoNothing = conflict.DoNothing + u.onConflict.DoUpdates = conflict.DoUpdates + u.onConflict.UpdateAll = conflict.UpdateAll } - return b + return u } -func (b *UpdateBuilder[T]) Scopes(tx *gorm.DB) *gorm.DB { - if b.selects != nil { - tx = tx.Select(b.selects) +func (u *UpdateBuilder[T]) Scopes(tx *gorm.DB) *gorm.DB { + if u.selects != nil { + tx = tx.Select(u.selects) } - if b.omits != nil { - tx = tx.Omit(b.omits...) + if u.omits != nil { + tx = tx.Omit(u.omits...) } - return b.Expr.Scopes(tx) + return u.expr.Scopes(tx) } -func (b *UpdateBuilder[T]) Commit(values map[string]any) (int64, error) { +func (u *UpdateBuilder[T]) Commit(values map[string]any) (int64, error) { var entity T - res := b.db.Model(&entity).Scopes(b.Scopes).Updates(values) + res := u.db.Model(&entity).Scopes(u.Scopes).Updates(values) if err := res.Error; err != nil { return res.RowsAffected, err }