Documentation
¶
Overview ¶
alias.go - v0.8.0 alias 体系核心类型与函数
Package gplus 是基于 GORM 的泛型增强库,提供类型安全的查询构建器、 Repository 模式和数据权限规则注入等功能。
快速开始 ¶
定义模型并创建 Repository:
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"column:name"`
Email string `gorm:"column:email"`
Age int `gorm:"column:age"`
}
repo := gplus.NewRepository[uint, User](db)
查询构建 ¶
NewQuery 返回一个类型安全的查询构建器和模型单例指针。 字段指针必须来自该返回的 *T 实例:
q, m := gplus.NewQuery[User](ctx) q.Eq(&m.Name, "Alice").Gt(&m.Age, 18).Order(&m.ID, false) users, err := repo.List(q)
更新构建 ¶
u, m := gplus.NewUpdater[User](ctx) u.Eq(&m.ID, 1).Set(&m.Name, "Bob") affected, err := repo.UpdateByCond(u)
分页 ¶
q, _ := gplus.NewQuery[User](ctx) list, total, err := repo.Page(q.Limit(10).Offset(0), false)
聚合 ¶
q, m := gplus.NewQuery[User](ctx) q.Gt(&m.Age, 0) avg, err := gplus.Avg[User, float64, uint](repo, q, &m.Age)
事务 ¶
err := db.Transaction(func(tx *gorm.DB) error {
_, err := repo.SaveTx(ctx, &user, tx)
return err
})
数据权限 ¶
通过 Context 注入 DataRule,对所有查询和写操作透明生效:
rules := []gplus.DataRule{{Column: "tenant_id", Value: 42}}
ctx = context.WithValue(ctx, gplus.DataRuleKey, rules)
调试 ¶
使用 ToSQL / ToCountSQL / ToUpdateSQL 获取最终执行的 SQL,无需实际执行:
q, m := gplus.NewQuery[User](ctx) q.Eq(&m.Name, "Alice") sql, args, err := gplus.ToSQL[uint, User](repo, q)
Alias 体系(v0.8.0) ¶
通过 As[X](q, name) 创建 alias 实例(独立于规范单例的 *X), 支持跨表列引用 / 自连接 / correlated EXISTS。
详见 docs/superpowers/specs/2026-05-06-alias-system-design.md。
Package gplus — Query-chain-safe 投影查询 API ¶
本文件提供 FindAs / FindOneAs / FindAsTx / FindOneAsTx 四个包级泛型函数, 让用户能写"投影查询走 GORM Query callback chain",避开 db.Scan / db.Row / db.Rows 绕过 Query chain 的隐患(导致下游 isolation/审计 callback 不触发)。
详见 docs/superpowers/specs/2026-05-01-scan-callback-fix-design.md
Index ¶
- Constants
- Variables
- func As[X any](q AnyQuery, alias string) *X
- func Avg[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
- func AvgTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
- func FindAs[T any, Dest any, D comparable](r *Repository[D, T], q *Query[T], dest *[]Dest) error
- func FindAsTx[T any, Dest any, D comparable](r *Repository[D, T], q *Query[T], dest *[]Dest, tx *gorm.DB) error
- func FindOneAs[T any, Dest any, D comparable](r *Repository[D, T], q *Query[T], dest *Dest) error
- func FindOneAsTx[T any, Dest any, D comparable](r *Repository[D, T], q *Query[T], dest *Dest, tx *gorm.DB) error
- func IsNotFound(err error) bool
- func Max[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
- func MaxTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
- func Min[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
- func MinTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
- func Pluck[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) ([]R, error)
- func PluckTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) ([]R, error)
- func RegisterModel(models ...any)
- func Sum[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
- func SumTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
- type AnyQuery
- type ColumnInfo
- type DataRule
- type OnConflict
- type Query
- func (q *Query[T]) And(fn func(sub *Query[T])) *Query[T]
- func (q *Query[T]) Between(col any, val1 any, val2 any) *Query[T]
- func (q *Query[T]) BuildQuery() func(db *gorm.DB) *gorm.DB
- func (q *Query[T]) BuildQueryDB(db *gorm.DB) *gorm.DB
- func (q *Query[T]) Clear()
- func (q *Query[T]) Context() context.Context
- func (q *Query[T]) CrossJoin(table string) *Query[T]deprecated
- func (q *Query[T]) CrossJoinAs(alias any) *Query[T]
- func (q *Query[T]) DataRuleBuilder() *Query[T]
- func (q *Query[T]) Distinct(cols ...any) *Query[T]
- func (q *Query[T]) Eq(col any, val any) *Query[T]
- func (q *Query[T]) EqSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) Exists(sub Subquerier) *Query[T]
- func (q *Query[T]) FullJoin(table string, on string, args ...any) *Query[T]deprecated
- func (q *Query[T]) FullJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
- func (q *Query[T]) Ge(col any, val any) *Query[T]
- func (q *Query[T]) GetError() error
- func (q *Query[T]) Group(cols ...any) *Query[T]
- func (q *Query[T]) Gt(col any, val any) *Query[T]
- func (q *Query[T]) GtSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) GteSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) Having(col string, op string, val any) *Query[T]
- func (q *Query[T]) HavingGroup(fn func(sub *Query[T])) *Query[T]
- func (q *Query[T]) In(col any, val any) *Query[T]
- func (q *Query[T]) InSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) InnerJoin(table string, on string, args ...any) *Query[T]deprecated
- func (q *Query[T]) InnerJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
- func (q *Query[T]) IsEmpty() bool
- func (q *Query[T]) IsNotNull(col any) *Query[T]
- func (q *Query[T]) IsNull(col any) *Query[T]
- func (q *Query[T]) IsUnscoped() bool
- func (q *Query[T]) Le(col any, val any) *Query[T]
- func (q *Query[T]) LeftJoin(table string, on string, args ...any) *Query[T]deprecated
- func (q *Query[T]) LeftJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
- func (q *Query[T]) Like(col any, val string) *Query[T]
- func (q *Query[T]) LikeLeft(col any, val string) *Query[T]
- func (q *Query[T]) LikeRight(col any, val string) *Query[T]
- func (q *Query[T]) Limit(limit int) *Query[T]
- func (q *Query[T]) LockRead() *Query[T]
- func (q *Query[T]) LockWithOpt(strength, options string) *Query[T]
- func (q *Query[T]) LockWrite() *Query[T]
- func (q *Query[T]) Lt(col any, val any) *Query[T]
- func (q *Query[T]) LtSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) LteSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) NaturalJoin(table string) *Query[T]deprecated
- func (q *Query[T]) NaturalJoinAs(alias any) *Query[T]
- func (q *Query[T]) Ne(col any, val any) *Query[T]
- func (q *Query[T]) NeSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) NotBetween(col any, val1 any, val2 any) *Query[T]
- func (q *Query[T]) NotExists(sub Subquerier) *Query[T]
- func (q *Query[T]) NotIn(col any, val any) *Query[T]
- func (q *Query[T]) NotInSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) NotLike(col any, val string) *Query[T]
- func (q *Query[T]) Offset(offset int) *Query[T]
- func (q *Query[T]) Omit(cols ...any) *Query[T]
- func (q *Query[T]) Or(fn func(sub *Query[T])) *Query[T]
- func (q *Query[T]) OrBetween(col any, val1 any, val2 any) *Query[T]
- func (q *Query[T]) OrEq(col any, val any) *Query[T]
- func (q *Query[T]) OrEqSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrExists(sub Subquerier) *Query[T]
- func (q *Query[T]) OrGe(col any, val any) *Query[T]
- func (q *Query[T]) OrGt(col any, val any) *Query[T]
- func (q *Query[T]) OrGtSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrGteSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrHaving(col string, op string, val any) *Query[T]
- func (q *Query[T]) OrIn(col any, val any) *Query[T]
- func (q *Query[T]) OrInSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrIsNotNull(col any) *Query[T]
- func (q *Query[T]) OrIsNull(col any) *Query[T]
- func (q *Query[T]) OrLe(col any, val any) *Query[T]
- func (q *Query[T]) OrLike(col any, val string) *Query[T]
- func (q *Query[T]) OrLikeLeft(col any, val string) *Query[T]
- func (q *Query[T]) OrLikeRight(col any, val string) *Query[T]
- func (q *Query[T]) OrLt(col any, val any) *Query[T]
- func (q *Query[T]) OrLtSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrLteSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrNe(col any, val any) *Query[T]
- func (q *Query[T]) OrNeSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrNotBetween(col any, val1 any, val2 any) *Query[T]
- func (q *Query[T]) OrNotExists(sub Subquerier) *Query[T]
- func (q *Query[T]) OrNotIn(col any, val any) *Query[T]
- func (q *Query[T]) OrNotInSub(col any, sub Subquerier) *Query[T]
- func (q *Query[T]) OrNotLike(col any, val string) *Query[T]
- func (q *Query[T]) OrWhereRaw(sql string, args ...any) *Query[T]
- func (q *Query[T]) Order(col any, isAsc bool) *Query[T]
- func (q *Query[T]) OrderRaw(expr string) *Query[T]
- func (q *Query[T]) OuterJoin(table string, on string, args ...any) *Query[T]deprecated
- func (q *Query[T]) OuterJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
- func (q *Query[T]) Page(page, pageSize int) *Query[T]
- func (q *Query[T]) Preload(column string, args ...any) *Query[T]
- func (q *Query[T]) RightJoin(table string, on string, args ...any) *Query[T]deprecated
- func (q *Query[T]) RightJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
- func (q *Query[T]) Select(cols ...any) *Query[T]
- func (q *Query[T]) SelectRaw(expr string) *Query[T]
- func (q *Query[T]) Table(name string) *Query[T]
- func (q *Query[T]) ToCountSQL(db *gorm.DB) (string, error)
- func (q *Query[T]) ToDB(db *gorm.DB) *gorm.DB
- func (q *Query[T]) ToSQL(db *gorm.DB) (string, error)
- func (q *Query[T]) Unscoped() *Query[T]
- func (q *Query[T]) WhereRaw(sql string, args ...any) *Query[T]
- func (q *Query[T]) WithScope(fn func(*gorm.DB) *gorm.DB) *Query[T]
- type Repository
- func (r *Repository[D, T]) Chunk(q *Query[T], batchSize int, fn func([]T) error) error
- func (r *Repository[D, T]) ChunkTx(q *Query[T], batchSize int, tx *gorm.DB, fn func([]T) error) error
- func (r *Repository[D, T]) Count(q *Query[T]) (int64, error)
- func (r *Repository[D, T]) CountTx(q *Query[T], tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) CreateBatch(ctx context.Context, entities []*T, batchSize int) error
- func (r *Repository[D, T]) CreateBatchTx(ctx context.Context, entities []*T, batchSize int, tx *gorm.DB) error
- func (r *Repository[D, T]) DecrBy(u *Updater[T], col any, delta int64) (int64, error)
- func (r *Repository[D, T]) DecrByTx(u *Updater[T], col any, delta int64, tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) DeleteByCond(q *Query[T]) (int64, error)
- func (r *Repository[D, T]) DeleteByCondTx(q *Query[T], tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) DeleteById(ctx context.Context, id D) (int64, error)
- func (r *Repository[D, T]) DeleteByIdTx(ctx context.Context, id D, tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) DeleteByIds(ctx context.Context, ids []D) (int64, error)
- func (r *Repository[D, T]) DeleteByIdsTx(ctx context.Context, ids []D, tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) Exists(q *Query[T]) (bool, error)
- func (r *Repository[D, T]) ExistsTx(q *Query[T], tx *gorm.DB) (bool, error)
- func (r *Repository[D, T]) FirstOrCreate(q *Query[T], defaults *T) (data T, created bool, err error)
- func (r *Repository[D, T]) FirstOrUpdate(q *Query[T], u *Updater[T], defaults *T) (data T, created bool, err error)
- func (r *Repository[D, T]) GetById(ctx context.Context, id D) (T, error)
- func (r *Repository[D, T]) GetByIdTx(ctx context.Context, id D, tx *gorm.DB) (data T, err error)
- func (r *Repository[D, T]) GetByIds(ctx context.Context, ids []D) ([]T, error)
- func (r *Repository[D, T]) GetByIdsTx(ctx context.Context, ids []D, tx *gorm.DB) ([]T, error)
- func (r *Repository[D, T]) GetByLock(q *Query[T], tx *gorm.DB) (*T, error)
- func (r *Repository[D, T]) GetDB() *gorm.DB
- func (r *Repository[D, T]) GetOne(q *Query[T]) (data T, err error)
- func (r *Repository[D, T]) GetOneTx(q *Query[T], tx *gorm.DB) (data T, err error)
- func (r *Repository[D, T]) IncrBy(u *Updater[T], col any, delta int64) (int64, error)
- func (r *Repository[D, T]) IncrByTx(u *Updater[T], col any, delta int64, tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) InsertBatchOnConflict(ctx context.Context, entities []T, oc OnConflict) error
- func (r *Repository[D, T]) InsertBatchOnConflictTx(ctx context.Context, entities []T, oc OnConflict, tx *gorm.DB) error
- func (r *Repository[D, T]) InsertOnConflict(ctx context.Context, entity *T, oc OnConflict) error
- func (r *Repository[D, T]) InsertOnConflictTx(ctx context.Context, entity *T, oc OnConflict, tx *gorm.DB) error
- func (r *Repository[D, T]) Last(q *Query[T]) (data T, err error)
- func (r *Repository[D, T]) LastTx(q *Query[T], tx *gorm.DB) (data T, err error)
- func (r *Repository[D, T]) List(q *Query[T]) (data []T, err error)
- func (r *Repository[D, T]) ListMap(q *Query[T], keyFn func(T) D) (map[D]T, error)
- func (r *Repository[D, T]) ListMapTx(q *Query[T], keyFn func(T) D, tx *gorm.DB) (map[D]T, error)
- func (r *Repository[D, T]) ListTx(q *Query[T], tx *gorm.DB) (data []T, err error)
- func (r *Repository[D, T]) NewQuery(ctx context.Context) (*Query[T], *T)
- func (r *Repository[D, T]) NewQueryAs(ctx context.Context, alias string) (*Query[T], *T)
- func (r *Repository[D, T]) NewUpdater(ctx context.Context) (*Updater[T], *T)
- func (r *Repository[D, T]) Page(q *Query[T], skipCount bool) (data []T, total int64, err error)
- func (r *Repository[D, T]) PageTx(q *Query[T], skipCount bool, tx *gorm.DB) (data []T, total int64, err error)
- func (r *Repository[D, T]) RawExec(ctx context.Context, sql string, args ...any) (int64, error)
- func (r *Repository[D, T]) RawExecTx(ctx context.Context, tx *gorm.DB, sql string, args ...any) (int64, error)
- func (r *Repository[D, T]) RawQuery(ctx context.Context, sql string, args ...any) ([]T, error)
- func (r *Repository[D, T]) RawQueryTx(ctx context.Context, tx *gorm.DB, sql string, args ...any) ([]T, error)
- func (r *Repository[D, T]) RawScan(ctx context.Context, dest any, sql string, args ...any) error
- func (r *Repository[D, T]) RawScanTx(ctx context.Context, tx *gorm.DB, dest any, sql string, args ...any) error
- func (r *Repository[D, T]) Restore(ctx context.Context, id D) (int64, error)
- func (r *Repository[D, T]) RestoreByCond(q *Query[T]) (int64, error)
- func (r *Repository[D, T]) RestoreByCondTx(q *Query[T], tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) RestoreTx(ctx context.Context, id D, tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) Save(ctx context.Context, entity *T) error
- func (r *Repository[D, T]) SaveBatch(ctx context.Context, entities []T) error
- func (r *Repository[D, T]) SaveBatchTx(ctx context.Context, entities []T, tx *gorm.DB) error
- func (r *Repository[D, T]) SaveTx(ctx context.Context, entity *T, tx *gorm.DB) error
- func (r *Repository[D, T]) ToCountSQL(q *Query[T]) (string, error)
- func (r *Repository[D, T]) ToSQL(q *Query[T]) (string, error)
- func (r *Repository[D, T]) ToUpdateSQL(u *Updater[T]) (string, error)
- func (r *Repository[D, T]) Transaction(ctx context.Context, fn func(tx *gorm.DB) error) error
- func (r *Repository[D, T]) UpdateByCond(u *Updater[T]) (int64, error)
- func (r *Repository[D, T]) UpdateByCondTx(u *Updater[T], tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) UpdateById(ctx context.Context, entity *T) error
- func (r *Repository[D, T]) UpdateByIdTx(ctx context.Context, entity *T, tx *gorm.DB) error
- func (r *Repository[D, T]) UpdateByIds(ctx context.Context, ids []D, u *Updater[T]) (int64, error)
- func (r *Repository[D, T]) UpdateByIdsTx(ctx context.Context, ids []D, u *Updater[T], tx *gorm.DB) (int64, error)
- func (r *Repository[D, T]) Upsert(ctx context.Context, entity *T) error
- func (r *Repository[D, T]) UpsertBatch(ctx context.Context, entities []T) error
- func (r *Repository[D, T]) UpsertBatchTx(ctx context.Context, entities []T, tx *gorm.DB) error
- func (r *Repository[D, T]) UpsertTx(ctx context.Context, entity *T, tx *gorm.DB) error
- func (r *Repository[D, T]) WithTx(tx *gorm.DB) *Repository[D, T]
- type ScopeBuilder
- type Subquerier
- type Updater
- func (u *Updater[T]) And(fn func(sub *Updater[T])) *Updater[T]
- func (u *Updater[T]) Between(col any, v1, v2 any) *Updater[T]
- func (u *Updater[T]) Clear()
- func (u *Updater[T]) Context() context.Context
- func (u *Updater[T]) DataRuleBuilder() *Updater[T]
- func (u *Updater[T]) Eq(col any, val any) *Updater[T]
- func (u *Updater[T]) EqSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) Exists(sub Subquerier) *Updater[T]
- func (u *Updater[T]) Ge(col any, val any) *Updater[T]
- func (u *Updater[T]) GetError() error
- func (u *Updater[T]) Gt(col any, val any) *Updater[T]
- func (u *Updater[T]) GtSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) GteSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) In(col any, val any) *Updater[T]
- func (u *Updater[T]) InSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) InnerJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Updater[T]
- func (u *Updater[T]) IsEmpty() bool
- func (u *Updater[T]) IsNotNull(col any) *Updater[T]
- func (u *Updater[T]) IsNull(col any) *Updater[T]
- func (u *Updater[T]) Le(col any, val any) *Updater[T]
- func (u *Updater[T]) LeftJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Updater[T]
- func (u *Updater[T]) Like(col any, val string) *Updater[T]
- func (u *Updater[T]) LikeLeft(col any, val string) *Updater[T]
- func (u *Updater[T]) LikeRight(col any, val string) *Updater[T]
- func (u *Updater[T]) Lt(col any, val any) *Updater[T]
- func (u *Updater[T]) LtSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) LteSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) Ne(col any, val any) *Updater[T]
- func (u *Updater[T]) NeSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) NotBetween(col any, v1, v2 any) *Updater[T]
- func (u *Updater[T]) NotExists(sub Subquerier) *Updater[T]
- func (u *Updater[T]) NotIn(col any, val any) *Updater[T]
- func (u *Updater[T]) NotInSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) NotLike(col any, val string) *Updater[T]
- func (u *Updater[T]) Omit(cols ...any) *Updater[T]
- func (u *Updater[T]) Or(fn func(sub *Updater[T])) *Updater[T]
- func (u *Updater[T]) OrBetween(col any, v1, v2 any) *Updater[T]
- func (u *Updater[T]) OrEq(col any, val any) *Updater[T]
- func (u *Updater[T]) OrEqSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrExists(sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrGe(col any, val any) *Updater[T]
- func (u *Updater[T]) OrGt(col any, val any) *Updater[T]
- func (u *Updater[T]) OrGtSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrGteSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrIn(col any, val any) *Updater[T]
- func (u *Updater[T]) OrInSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrIsNotNull(col any) *Updater[T]
- func (u *Updater[T]) OrIsNull(col any) *Updater[T]
- func (u *Updater[T]) OrLe(col any, val any) *Updater[T]
- func (u *Updater[T]) OrLike(col any, val string) *Updater[T]
- func (u *Updater[T]) OrLikeLeft(col any, val string) *Updater[T]
- func (u *Updater[T]) OrLikeRight(col any, val string) *Updater[T]
- func (u *Updater[T]) OrLt(col any, val any) *Updater[T]
- func (u *Updater[T]) OrLtSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrLteSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrNe(col any, val any) *Updater[T]
- func (u *Updater[T]) OrNeSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrNotBetween(col any, v1, v2 any) *Updater[T]
- func (u *Updater[T]) OrNotExists(sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrNotIn(col any, val any) *Updater[T]
- func (u *Updater[T]) OrNotInSub(col any, sub Subquerier) *Updater[T]
- func (u *Updater[T]) OrNotLike(col any, val string) *Updater[T]
- func (u *Updater[T]) OrWhereRaw(sql string, args ...any) *Updater[T]
- func (u *Updater[T]) Select(cols ...any) *Updater[T]
- func (u *Updater[T]) Set(col any, val any) *Updater[T]
- func (u *Updater[T]) SetExpr(col any, expr string, args ...any) *Updater[T]
- func (u *Updater[T]) SetMap(m map[string]any) *Updater[T]
- func (u *Updater[T]) Table(name string) *Updater[T]
- func (u *Updater[T]) ToSQL(db *gorm.DB) (string, error)
- func (u *Updater[T]) Unscoped() *Updater[T]
- func (u *Updater[T]) UpdateMap() map[string]any
- func (u *Updater[T]) WhereRaw(sql string, args ...any) *Updater[T]
- func (u *Updater[T]) WithScope(fn func(*gorm.DB) *gorm.DB) *Updater[T]
Examples ¶
Constants ¶
const ( OpEq = "=" OpNe = "<>" OpGt = ">" OpGe = ">=" OpLt = "<" OpLe = "<=" OpLike = "LIKE" OpNotLike = "NOT LIKE" OpIn = "IN" OpNotIn = "NOT IN" OpIsNull = "IS NULL" OpIsNotNull = "IS NOT NULL" OpBetween = "BETWEEN" OpNotBetween = "NOT BETWEEN" KeyAnd = "AND" KeyOr = "OR" KeyDesc = "DESC" KeyAsc = "ASC" // JoinLeft 左连接 返回左表中的所有记录,即使右表中没有匹配的记录(保留左表)。 JoinLeft = "LEFT JOIN" // JoinRight 右连接 返回右表中的所有记录,即使左表中没有匹配的记录(保留右表)。 JoinRight = "RIGHT JOIN" // JoinInner 内连接 只返回两个表中都存在的记录 (交集)。 JoinInner = "INNER JOIN" // JoinOuter 注意:裸 "OUTER JOIN" 不是标准 SQL,MySQL/PostgreSQL/SQLite 均不支持, // 调用此常量将导致数据库语法错误。如需外连接,请使用 JoinFull ("FULL OUTER JOIN")。 JoinOuter = "OUTER JOIN" // JoinNatural 自然连接 返回两个表中相同列名和数据类型的所有记录。 JoinNatural = "NATURAL JOIN" // JoinFull 全连接 返回左表中的所有记录,即使右表中没有匹配的记录。 JoinFull = "FULL OUTER JOIN" // JoinCross 交叉连接 返回两个表中的所有记录,不论它们是否匹配。 // 因为其就是把表A和表B的数据进行一个N*M的组合,即笛卡尔积。 // 表达式如下:SELECT * FROM TableA CROSS JOIN TableB // 这个笛卡尔乘积会产生 4 x 4 = 16 条记录 JoinCross = "CROSS JOIN" )
Variables ¶
var ( ErrAliasDuplicate = errors.New("gplus: alias name already registered in this query chain") ErrAliasInvalidName = errors.New("gplus: invalid alias name (must match [a-zA-Z_][a-zA-Z0-9_]{0,31})") ErrFieldAddrUnregistered = errors.New("gplus: field address not registered to any model or alias in this query chain") ErrAliasNotInChain = errors.New("gplus: alias instance does not belong to this query chain") ErrSubqueryOuterNil = errors.New("gplus: SubQuery outer is nil") ErrAliasQueryNil = errors.New("gplus: As query is nil") ErrAliasRevoked = errors.New("gplus: alias instance has been revoked by Clear()") )
新增哨兵错误(7 个)
var ( ErrQueryNil = errors.New("gplus: query cannot be nil") ErrRawSQLEmpty = errors.New("gplus: raw sql cannot be empty") ErrDeleteEmpty = errors.New("gplus: delete content is empty") ErrUpdateEmpty = errors.New("gplus: update content is empty") ErrUpdateNoCondition = errors.New("gplus: update requires at least one condition to prevent full-table update") ErrTransactionReq = errors.New("gplus: locking query must be executed within a transaction") ErrDefaultsNil = errors.New("gplus: defaults cannot be nil, use &T{} to create a zero-value record explicitly") ErrRestoreEmpty = errors.New("gplus: restore condition is empty") ErrOnConflictInvalid = errors.New("gplus: OnConflict config invalid: DoNothing is mutually exclusive with DoUpdates/DoUpdateAll/UpdateExprs; DoUpdateAll is mutually exclusive with DoUpdates/UpdateExprs") ErrOptimisticLock = errors.New("gplus: optimistic lock conflict (version mismatch or row not found)") ErrSubqueryNil = errors.New("gplus: subquery is nil") )
var ( ErrColumnNotFound = errors.New("gplus: column name not found for pointer") ErrInvalidPointer = errors.New("gplus: argument must be a struct field pointer") ErrColumnEmpty = errors.New("gplus: column name cannot be empty") )
var DataRuleKey = dataRuleKey{}
DataRuleKey 是用于在 context.Context 中存储 []DataRule 的键。 使用示例:ctx = context.WithValue(ctx, gplus.DataRuleKey, rules)
var ErrFindOneAsConflict = errors.New("gplus: FindOneAs 不可与 q.Limit() / q.Page() 组合调用")
ErrFindOneAsConflict 表示 FindOneAs 与 q.Limit() / q.Page() 组合调用。 内部 First 会追加 LIMIT 1,与已有 LIMIT 叠加,部分 DB 行为未定义。
Functions ¶
func As ¶ added in v0.8.0
As 在 q(含 outerQueryRef 链)上注册一个 X 类型的 alias 实例。
错误处理:
- q == nil:panic ErrAliasQueryNil(N5:API 入口编程错误,无 q 可挂错误)
- name 不符合白名单正则:累积 ErrAliasInvalidName,返回规范单例 fallback
- name 已在链中存在:累积 ErrAliasDuplicate,返回首次注册实例(决策 1B)
返回的 *X 实例字段地址独立于规范单例,仅用于取字段地址。
func Avg ¶ added in v0.3.0
func Avg[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
Avg 对指定列求平均值,R 为数值类型。
【R 支持类型】int64 / float64 / string 安全;MySQL DECIMAL 列建议 R=float64 (database/sql 通过 strconv 路径正确转换);time.Time / sql.NullX 作为 R 无意义。
【callback chain】走 Query callback chain(v0.7.0 起),下游 isolation/审计 callback 会触发。
【col 字符串警告】传字符串列名时 gplus 不做白名单校验,禁止将用户输入直接传入 col。
func AvgTx ¶ added in v0.3.0
func AvgTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
AvgTx 支持事务的列平均值
func FindAs ¶ added in v0.7.0
func FindAs[T any, Dest any, D comparable]( r *Repository[D, T], q *Query[T], dest *[]Dest, ) error
FindAs 投影查询(多行)。dest 必须是 *[]Element 切片指针。
走 GORM Query callback chain,下游挂在 Query chain 上的隔离/审计 callback 会触发。
【迁移提示】若现有代码用 q.ToDB(db).Model(&T{}).Scan(&rows) / .Rows() / .Row(), 必须改用 gplus.FindAs。前者绕过 Query callback chain,会导致下游隔离/审计 callback 不触发,可能引发跨租户数据泄露 / 审计日志缺失(详见 README "已知陷阱")。
【副作用】调用 FindAs 后 q.conditions 会被永久追加 DataRule 条件 (dataRuleApplied 保护幂等),q 不应再跨不同 ctx 复用。与 List/Sum 等行为一致。
【调用形态】Go 1.18+ 类型推导后无需写类型参数:
var rows []UserVO err := gplus.FindAs(repo, q, &rows)
func FindAsTx ¶ added in v0.7.0
func FindAsTx[T any, Dest any, D comparable]( r *Repository[D, T], q *Query[T], dest *[]Dest, tx *gorm.DB, ) error
FindAsTx 支持事务的 FindAs。tx 为 nil 时与 FindAs 等价。
func FindOneAs ¶ added in v0.7.0
func FindOneAs[T any, Dest any, D comparable]( r *Repository[D, T], q *Query[T], dest *Dest, ) error
FindOneAs 投影查询(单行)。dest 是 *Element。
无匹配时返回 gorm.ErrRecordNotFound(与 GetById 既有语义一致)。
走 GORM Query callback chain,下游挂在 Query chain 上的隔离/审计 callback 会触发。
【迁移提示】若现有代码用 q.ToDB(db).Model(&T{}).Limit(1).Scan(&one),必须改用 gplus.FindOneAs。前者绕过 Query chain,可能引发跨租户数据泄露 / 审计日志缺失。
【约束】FindOneAs 不可与 q.Limit() / q.Page() 组合 —— 内部 First 会追加 LIMIT 1, 与已有 LIMIT 叠加部分 DB 行为未定义。组合调用会立即返回 ErrFindOneAsConflict。
【实测确认】GORM v1.31.x First(dest) 不会用 dest 的 schema 覆盖已设置的 Model(new(T));下游 isolation callback 拿到的 Schema.Table 仍为 T 表名。 (由 TestGORMCallbackBehaviorProbe 永久守护)
func FindOneAsTx ¶ added in v0.7.0
func FindOneAsTx[T any, Dest any, D comparable]( r *Repository[D, T], q *Query[T], dest *Dest, tx *gorm.DB, ) error
FindOneAsTx 支持事务的 FindOneAs。
func Max ¶ added in v0.3.0
func Max[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
Max 对指定列求最大值,R 为数值类型。
【R 支持类型】int64 / float64 / string 安全;MySQL DECIMAL 列建议 R=float64 (database/sql 通过 strconv 路径正确转换);time.Time / sql.NullX 作为 R 无意义。
【callback chain】走 Query callback chain(v0.7.0 起),下游 isolation/审计 callback 会触发。
【col 字符串警告】传字符串列名时 gplus 不做白名单校验,禁止将用户输入直接传入 col。
func MaxTx ¶ added in v0.3.0
func MaxTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
MaxTx 支持事务的列最大值
func Min ¶ added in v0.3.0
func Min[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
Min 对指定列求最小值,R 为数值类型。
【R 支持类型】int64 / float64 / string 安全;MySQL DECIMAL 列建议 R=float64 (database/sql 通过 strconv 路径正确转换);time.Time / sql.NullX 作为 R 无意义。
【callback chain】走 Query callback chain(v0.7.0 起),下游 isolation/审计 callback 会触发。
【col 字符串警告】传字符串列名时 gplus 不做白名单校验,禁止将用户输入直接传入 col。
func MinTx ¶ added in v0.3.0
func MinTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) (R, error)
MinTx 支持事务的列最小值
func Pluck ¶
func Pluck[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) ([]R, error)
Pluck 提取单列值到泛型切片中,R 为列元素类型。
【R 支持类型】int64 / float64 / string 安全;MySQL DECIMAL 列建议 R=float64 (database/sql 通过 strconv 路径正确转换);time.Time / sql.NullX 作为 R 无意义。
【callback chain】走 Query callback chain(v0.7.0 起),下游 isolation/审计 callback 会触发。
【col 字符串警告】传字符串列名时 gplus 不做白名单校验,禁止将用户输入直接传入 col。
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/glebarez/sqlite"
"github.com/yi-nanping/gplus"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Article 示例模型
type Article struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Title string `gorm:"column:title"`
Author string `gorm:"column:author"`
Views int `gorm:"column:views"`
Deleted gorm.DeletedAt
}
func openExampleDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
log.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&Article{}); err != nil {
log.Fatalf("migrate: %v", err)
}
return db
}
func main() {
db := openExampleDB()
repo := gplus.NewRepository[uint, Article](db)
ctx := context.Background()
db.Create(&Article{Title: "A", Author: "Alice", Views: 10})
db.Create(&Article{Title: "B", Author: "Alice", Views: 20})
q, m := gplus.NewQuery[Article](ctx)
q.Eq(&m.Author, "Alice").Order(&m.Views, true)
titles, err := gplus.Pluck[Article, string, uint](repo, q, &m.Title)
if err != nil {
log.Fatal(err)
}
fmt.Println(titles)
}
Output: [A B]
func PluckTx ¶
func PluckTx[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any, tx *gorm.DB) ([]R, error)
PluckTx 在指定事务中提取单列值到泛型切片中
func RegisterModel ¶
func RegisterModel(models ...any)
RegisterModel 注册模型,解析并缓存字段映射关系。 通常在应用启动时显式调用,也可在首次查询时由框架自动触发。 注意:必须在任何并发查询(NewQuery/NewUpdater)开始前完成调用, 即在 http.ListenAndServe 或 goroutine 启动之前执行,否则可能出现竞态。 实际上,大多数场景无需显式调用本方法——框架会在首次 NewQuery/NewUpdater 时自动注册。 并发安全:多个 goroutine 同时注册同一类型时,只有第一个写入者的 指针会成为规范单例,其余调用无副作用。 传入 nil(无类型 nil)或 typed-nil 指针时会被静默跳过。
func Sum ¶ added in v0.3.0
func Sum[T any, R any, D comparable](r *Repository[D, T], q *Query[T], col any) (R, error)
Sum 对指定列求和,R 为数值类型。
【R 支持类型】int64 / float64 / string 安全;MySQL DECIMAL 列建议 R=float64 (database/sql 通过 strconv 路径正确转换);time.Time / sql.NullX 作为 R 无意义。
【callback chain】走 Query callback chain(v0.7.0 起),下游 isolation/审计 callback 会触发。
【col 字符串警告】传字符串列名时 gplus 不做白名单校验,禁止将用户输入直接传入 col。
Types ¶
type AnyQuery ¶ added in v0.8.0
type AnyQuery interface {
// contains filtered or unexported methods
}
AnyQuery 是 Query[T] 和 Updater[T] 的 phantom 标签接口 业务代码无法实现(gplusCore 返回 unexported 类型)
type ColumnInfo ¶
ColumnInfo 存储字段偏移量和列名的关系
type DataRule ¶
type DataRule struct {
Column string // 规则字段 (例如: "dept_id");仅允许字母/数字/下划线及单个点分隔的表名前缀(如 "table.col"),含括号或运算符的表达式会被拒绝
Condition string // 规则条件 (例如: "=", "IN", "LIKE")
Value string // 规则值 (例如: "1001");IN/NOT IN/BETWEEN 建议使用 Values
Values []string // IN/NOT IN/BETWEEN 的多值列表,优先于 Value 的逗号分隔解析
}
DataRule 对外开放的核心规则字段
type OnConflict ¶ added in v0.4.0
type OnConflict struct {
// Columns 冲突检测列(唯一索引/主键),支持字段指针或字符串列名。
Columns []any
// DoNothing 冲突时跳过,不执行任何更新(INSERT IGNORE / DO NOTHING)。
// 与 DoUpdates/DoUpdateAll/UpdateExprs 互斥。
DoNothing bool
// DoUpdates 冲突时按 EXCLUDED 覆盖指定列,支持字段指针或字符串列名。
// 与 DoNothing/DoUpdateAll 互斥,可与 UpdateExprs 组合。
DoUpdates []any
// DoUpdateAll 冲突时按 EXCLUDED 覆盖除主键外的所有列。
// 与 DoNothing/DoUpdates/UpdateExprs 互斥。
DoUpdateAll bool
// UpdateExprs 冲突时按自定义表达式更新,key 为列名,value 为 gorm.Expr 或普通值。
// 示例:{"count": gorm.Expr("count + VALUES(count)")}
// 可与 DoUpdates 组合,不可与 DoNothing/DoUpdateAll 共用。
UpdateExprs map[string]any
}
OnConflict 定义 INSERT ... ON CONFLICT 的冲突处理策略。 Columns 为空时:MySQL 按表内唯一索引自动判定;Postgres/SQLite 须显式指定冲突列。
type Query ¶
type Query[T any] struct { ScopeBuilder // contains filtered or unexported fields }
func NewQuery ¶
NewQuery 创建泛型查询构建器,同时返回类型 T 的规范实例指针。 所有字段指针参数(如 &model.Name)必须来自返回的 *T 实例。 ctx 用于传递请求级上下文(DataRule、超时等),可传 context.Background()。
func NewQueryAs ¶ added in v0.8.0
NewQueryAs 创建 Query 并给主表起 alias。
返回的 *T 是独立 alias 实例(字段地址绑定到 alias),而非规范单例; 使用 &t.Field 时解析为 "alias.col" 而非 "table.col"。
alias 必须满足 ^[a-zA-Z_][a-zA-Z0-9_]{0,31}$,否则累积 ErrAliasInvalidName。
func SubQuery ¶ added in v0.8.0
SubQuery 派生子查询。sub.outerQueryRef = outer,sub 主表 alias 自动设为表名(如 Order → "orders")。
错误处理:
- outer == nil:返回带 ErrSubqueryOuterNil 累积错误的 dud sub(H4:与 errs 哲学一致,不 panic)
sub.ctx 来自 outer.ctx(透传)。sub 不自动应用 outer 的 DataRule(保持 v0.6.0 既有语义)。
func SubQueryAs ¶ added in v0.8.0
SubQueryAs 派生子查询并指定主表 alias。 等价于 SubQuery + As(sub, alias),但合并为单步以避免双初始化。
错误处理:
- outer == nil:返回带 ErrSubqueryOuterNil 累积错误的 dud sub(H4)
- alias 不合法:由 NewQueryAs 内部 As 校验逻辑累积 ErrAliasInvalidName
func (*Query[T]) BuildQuery ¶ added in v0.8.0
BuildQuery 覆盖 ScopeBuilder.BuildQuery(promoted method),在闭包入口添加 v0.8.0 决策 1B errs 短路。
行为:
- 若 q.core.errs 非空(含 As 重名/Clear 后用残骸等错误):返回的 closure 调用时 直接 db.AddError 返回,不生成 SQL
- 若 q.core.errs 为空:与 ScopeBuilder.BuildQuery 既有行为一致(DataRule + 条件构建)
这确保所有 .Scopes(q.BuildQuery()) 生产路径(repo.GetById/List/Page/FindAs 等) 在 As 重名 / Clear 残骸 / 字段地址未注册等错误下自动短路,不依赖调用方先调 GetError。
func (*Query[T]) BuildQueryDB ¶ added in v0.8.0
BuildQueryDB 将当前 Query 的条件应用到 db 并返回带条件的 *gorm.DB。 v0.8.0 决策 1B:若 q.core.errs 非空(含 As 重名等错误),直接返回带聚合错误的 db,不生成 SQL。 防止重名 alias / Clear 后用 alias 等错误被快乐路径 SQL 静默掩盖。
与 BuildQuery()(返回闭包供 Scopes 使用)互补; BuildQueryDB 用于需要直接获得 *gorm.DB 的场景(如 DataRuleBuilder 链式调用末尾)。
func (*Query[T]) CrossJoin
deprecated
CrossJoin 交叉连接:返回笛卡尔积 注意:交叉连接通常不需要 ON 条件
Deprecated: use CrossJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) CrossJoinAs ¶ added in v0.8.0
CrossJoinAs 类型安全的 CROSS JOIN(无 ON 条件)
func (*Query[T]) DataRuleBuilder ¶
DataRuleBuilder 从上下文中提取规则并应用到查询中。 对同一个 Query 对象只执行一次,防止多次调用(如 Page 内的 Count+Find)重复追加条件。
func (*Query[T]) Distinct ¶
Distinct 去重 支持传入字段指针或字符串,例如:q.Distinct(&user.Name, &user.Age) 如果不传参数,则默认为 DISTINCT *
func (*Query[T]) EqSub ¶ added in v0.6.0
func (q *Query[T]) EqSub(col any, sub Subquerier) *Query[T]
EqSub 子查询:col = (subquery)。详见 InSub 关于 sub 生命周期约束。
func (*Query[T]) Exists ¶ added in v0.8.0
func (q *Query[T]) Exists(sub Subquerier) *Query[T]
Exists 添加 EXISTS 子查询条件(AND)。
sub 通常通过 SubQuery[X](q) 派生,能引用外层 q 的字段(相关子查询)。 sub == nil 时累积 ErrSubqueryNil;sub.GetError() 非空时在 BuildQuery 执行时透传到外层 GORM 错误链。
func (*Query[T]) FullJoin
deprecated
FullJoin 全外连接:返回左右表中所有的记录
Deprecated: use FullJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) FullJoinAs ¶ added in v0.8.0
func (q *Query[T]) FullJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
FullJoinAs 类型安全的 FULL JOIN
func (*Query[T]) GtSub ¶ added in v0.6.0
func (q *Query[T]) GtSub(col any, sub Subquerier) *Query[T]
GtSub > 子查询:col > (subquery)。详见 InSub。
func (*Query[T]) GteSub ¶ added in v0.6.0
func (q *Query[T]) GteSub(col any, sub Subquerier) *Query[T]
GteSub >= 子查询:col >= (subquery)。详见 InSub。
func (*Query[T]) HavingGroup ¶
HavingGroup 嵌套 Having
func (*Query[T]) InSub ¶ added in v0.6.0
func (q *Query[T]) InSub(col any, sub Subquerier) *Query[T]
InSub IN 子查询:col IN (subquery)。
sub 必须为类型安全 *Query[X];外部冒名实现被 gplusSubquery() guard 阻止。 sub 应在传入前完成构建(包括 Select/Where/DataRuleBuilder),传入后再修改会 反映到最终 SQL(延迟调用语义)。
sub 中需用 Select(&col) 限定单列;否则 GORM 运行时报多列错误。
func (*Query[T]) InnerJoin
deprecated
InnerJoin 内连接:仅返回两个表中匹配的记录(交集)
Deprecated: use InnerJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) InnerJoinAs ¶ added in v0.8.0
func (q *Query[T]) InnerJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
InnerJoinAs 类型安全的 INNER JOIN
func (*Query[T]) IsEmpty ¶
IsEmpty 判断是否为空查询(无任何类型安全条件)。 注意:仅检查通过 Eq/In/Between 等类型安全 API 添加的条件; 通过 WithScope 注入的自定义 scope 函数不计入此判断。
func (*Query[T]) LeftJoin
deprecated
LeftJoin 左连接:返回左表所有记录,即使右表无匹配
Deprecated: use LeftJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) LeftJoinAs ¶ added in v0.8.0
func (q *Query[T]) LeftJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
LeftJoinAs 类型安全的 LEFT JOIN(v0.8.0 alias 体系)。
alias 必须由 As[X](q, ...) 创建的实例,且属于当前 q 链; leftCol / rightCol 为任意一侧的字段指针(主表或副表均可); extraSQL 为额外的 ON 条件 SQL 片段,仅含 ? 占位符(如 "AND o.status = ?")—— 绝不直接拼接用户输入,extraSQL 本身不经 fmt.Sprintf 处理; extraArgs 对应占位符参数,走 GORM 参数化预编译,与 db.Joins(sql, args...) 同语义。
错误处理:alias 不属于 q 链时累积 ErrAliasNotInChain 并跳过该 JOIN(保持链式)。
func (*Query[T]) LockWithOpt ¶
LockWithOpt 高级加锁 (支持 NOWAIT 或 SKIP LOCKED) strength: "UPDATE" / "SHARE" options: "NOWAIT" / "SKIP LOCKED"
func (*Query[T]) LtSub ¶ added in v0.6.0
func (q *Query[T]) LtSub(col any, sub Subquerier) *Query[T]
LtSub < 子查询:col < (subquery)。详见 InSub。
func (*Query[T]) LteSub ¶ added in v0.6.0
func (q *Query[T]) LteSub(col any, sub Subquerier) *Query[T]
LteSub <= 子查询:col <= (subquery)。详见 InSub。
func (*Query[T]) NaturalJoin
deprecated
NaturalJoin 自然连接:基于相同列名自动匹配
Deprecated: use NaturalJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) NaturalJoinAs ¶ added in v0.8.0
NaturalJoinAs 类型安全的 NATURAL JOIN(无 ON 条件)
func (*Query[T]) NeSub ¶ added in v0.6.0
func (q *Query[T]) NeSub(col any, sub Subquerier) *Query[T]
NeSub <> 子查询:col <> (subquery)。详见 InSub。
func (*Query[T]) NotBetween ¶
NotBetween 区间查询(不包含边界)
func (*Query[T]) NotExists ¶ added in v0.8.0
func (q *Query[T]) NotExists(sub Subquerier) *Query[T]
NotExists 添加 NOT EXISTS 子查询条件(AND)。详见 Exists。
func (*Query[T]) NotInSub ¶ added in v0.6.0
func (q *Query[T]) NotInSub(col any, sub Subquerier) *Query[T]
NotInSub NOT IN 子查询。详见 InSub。
func (*Query[T]) OrEqSub ¶ added in v0.6.0
func (q *Query[T]) OrEqSub(col any, sub Subquerier) *Query[T]
OrEqSub 子查询(或)。详见 EqSub。
func (*Query[T]) OrExists ¶ added in v0.8.0
func (q *Query[T]) OrExists(sub Subquerier) *Query[T]
OrExists 添加 OR EXISTS 子查询条件。与 Exists 相同,但使用 OR 逻辑。
func (*Query[T]) OrGtSub ¶ added in v0.6.0
func (q *Query[T]) OrGtSub(col any, sub Subquerier) *Query[T]
OrGtSub > 子查询(或)。详见 GtSub。
func (*Query[T]) OrGteSub ¶ added in v0.6.0
func (q *Query[T]) OrGteSub(col any, sub Subquerier) *Query[T]
OrGteSub >= 子查询(或)。详见 GteSub。
func (*Query[T]) OrInSub ¶ added in v0.6.0
func (q *Query[T]) OrInSub(col any, sub Subquerier) *Query[T]
OrInSub IN 子查询(或)。详见 InSub。
func (*Query[T]) OrLikeLeft ¶
OrLikeLeft 左模糊查询(或)
func (*Query[T]) OrLikeRight ¶
OrLikeRight 右模糊查询(或)
func (*Query[T]) OrLtSub ¶ added in v0.6.0
func (q *Query[T]) OrLtSub(col any, sub Subquerier) *Query[T]
OrLtSub < 子查询(或)。详见 LtSub。
func (*Query[T]) OrLteSub ¶ added in v0.6.0
func (q *Query[T]) OrLteSub(col any, sub Subquerier) *Query[T]
OrLteSub <= 子查询(或)。详见 LteSub。
func (*Query[T]) OrNeSub ¶ added in v0.6.0
func (q *Query[T]) OrNeSub(col any, sub Subquerier) *Query[T]
OrNeSub <> 子查询(或)。详见 NeSub。
func (*Query[T]) OrNotBetween ¶
OrNotBetween 区间查询(不包含边界)(或)
func (*Query[T]) OrNotExists ¶ added in v0.8.0
func (q *Query[T]) OrNotExists(sub Subquerier) *Query[T]
OrNotExists 添加 OR NOT EXISTS 子查询条件。与 NotExists 相同,但使用 OR 逻辑。
func (*Query[T]) OrNotInSub ¶ added in v0.6.0
func (q *Query[T]) OrNotInSub(col any, sub Subquerier) *Query[T]
OrNotInSub NOT IN 子查询(或)。详见 InSub。
func (*Query[T]) OrWhereRaw ¶ added in v0.2.0
OrWhereRaw 添加原生 SQL 条件(OR)。 参数安全要求与 WhereRaw 相同。
func (*Query[T]) OrderRaw ¶ added in v0.2.0
OrderRaw 添加原生 ORDER BY 表达式,不经转义直接传入 GORM。 适用于含函数调用、CASE WHEN、NULLS LAST 等复杂排序场景。 调用顺序即为最终 SQL ORDER BY 的顺序,可与 Order 混用。 示例:q.OrderRaw("FIELD(status, 'active', 'pending')") 示例:q.OrderRaw("score DESC NULLS LAST") 注意:expr 参数由调用方负责安全性,不可直接拼接用户输入。
func (*Query[T]) OuterJoin
deprecated
OuterJoin 注意:裸 "OUTER JOIN" 不是标准 SQL,MySQL/PostgreSQL/SQLite 均不支持, 调用此方法将导致数据库语法错误。如需外连接,请使用 FullJoin ("FULL OUTER JOIN")。
Deprecated: use OuterJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) OuterJoinAs ¶ added in v0.8.0
func (q *Query[T]) OuterJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
OuterJoinAs 类型安全的 OUTER JOIN(部分方言别名为 FULL OUTER JOIN)
func (*Query[T]) Preload ¶
Preload 预加载关联数据 column: 结构体中的关联字段名(通常是字符串,如 "Orders" 或 "User.Role") args: 可选的过滤条件,例如只预加载状态为已支付的订单
func (*Query[T]) RightJoin
deprecated
RightJoin 右连接:返回右表所有记录,即使左表无匹配
Deprecated: use RightJoinAs for type-safe column references. Will be removed in v1.0. Still useful for joining subquery tables / function-returning tables / USING clauses where alias instances cannot represent the join target.
func (*Query[T]) RightJoinAs ¶ added in v0.8.0
func (q *Query[T]) RightJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Query[T]
RightJoinAs 类型安全的 RIGHT JOIN(参数语义同 LeftJoinAs)
func (*Query[T]) SelectRaw ¶ added in v0.6.0
SelectRaw 添加原生 SELECT 字段表达式。 expr 为原生 SQL 表达式,不经列名转义直接传入 GORM。 示例:q.SelectRaw("AVG(age)").SelectRaw("COUNT(*) as cnt") 注意:expr 参数由调用方负责安全性,不可直接拼接用户输入。
func (*Query[T]) ToCountSQL ¶ added in v0.4.0
ToCountSQL 将当前查询转换为 COUNT SQL 字符串,不执行实际查询。 对应 Page() 内部的 COUNT 路径,可用于确认分页计数时的实际 SQL。
func (*Query[T]) ToDB ¶
ToDB 将当前 Query 的条件转换为 GORM 的 DB 对象 注意:这不会执行查询,只会生成带有条件的 DB 实例,常用于子查询 1. 构建子查询 (查部门 ID) subQuery, _ := gplus.NewQuery[Dept](ctx) subQuery.Eq(&Dept.Name, "IT").Select(&Dept.Id) 2. 获取 DB 实例 (通常 Repository 会暴露 GetDB,或者直接从外部传入) 这里的 repo 是 UserRepo db := userRepo.GetDB() 3. 构建主查询 (查用户) mainQuery, _ := gplus.NewQuery[User](ctx) 【关键】:使用 ToDB 将 subQuery 转换为 GORM 对象,放入 In 条件中 mainQuery.In(&User.DeptId, subQuery.ToDB(db)) 4. 执行查询 users, err := userRepo.List(mainQuery)
func (*Query[T]) ToSQL ¶ added in v0.4.0
ToSQL 将当前查询转换为 SELECT SQL 字符串,不执行实际查询。 返回的 SQL 已将参数内联,仅用于调试展示,不可直接作为参数化查询使用。 db 提供方言信息(引号类型),不会发出任何网络请求。
func (*Query[T]) WhereRaw ¶ added in v0.2.0
WhereRaw 添加原生 SQL 条件(AND)。 sql 为完整条件片段,args 为参数绑定值,防止 SQL 注入。 示例:q.WhereRaw("YEAR(created_at) = ?", 2024) 注意:sql 参数由调用方负责安全性,不可直接拼接用户输入。
type Repository ¶
type Repository[D comparable, T any] struct { // contains filtered or unexported fields }
Repository 泛型仓储,提供标准 CRUD D: ID类型 (int, string, etc.), T: 实体类型
func NewRepository ¶
func NewRepository[D comparable, T any](db *gorm.DB) *Repository[D, T]
NewRepository 构造函数
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/glebarez/sqlite"
"github.com/yi-nanping/gplus"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Article 示例模型
type Article struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Title string `gorm:"column:title"`
Author string `gorm:"column:author"`
Views int `gorm:"column:views"`
Deleted gorm.DeletedAt
}
func openExampleDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
log.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&Article{}); err != nil {
log.Fatalf("migrate: %v", err)
}
return db
}
func main() {
db := openExampleDB()
repo := gplus.NewRepository[uint, Article](db)
ctx := context.Background()
// 插入记录
a := &Article{Title: "Hello gplus", Author: "Alice", Views: 100}
if err := repo.Save(ctx, a); err != nil {
log.Fatal(err)
}
// 按主键查询
got, err := repo.GetById(ctx, a.ID)
if err != nil {
log.Fatal(err)
}
fmt.Println(got.Title)
}
Output: Hello gplus
func (*Repository[D, T]) Chunk ¶ added in v0.3.0
func (r *Repository[D, T]) Chunk(q *Query[T], batchSize int, fn func([]T) error) error
Chunk 分批处理查询结果,每批调用 fn 一次。fn 返回非 nil 错误时立即终止并返回该错误。 batchSize 建议在 100-1000 之间,过小会增加 DB 往返次数,过大会占用大量内存。
内部基于 GORM FindInBatches,使用主键游标分页(WHERE id > lastID),性能优于 OFFSET 分页。 主键类型说明:
- 自增 int:最优,单调递增,索引连续扫描。
- UUID v7 / 时间有序字符串:接近 int,近似有序,性能良好。
- UUID v4 / 随机字符串:功能正确,但随机分布导致索引跳跃,性能弱于 int。
- 复合主键:GORM 仅用第一个主键字段做游标,可能漏行,不建议使用 Chunk,请改用 Page()。
func (*Repository[D, T]) ChunkTx ¶ added in v0.3.0
func (r *Repository[D, T]) ChunkTx(q *Query[T], batchSize int, tx *gorm.DB, fn func([]T) error) error
ChunkTx 支持事务的分批处理。tx=nil 时降级为普通连接,行为等同 Chunk。
func (*Repository[D, T]) Count ¶
func (r *Repository[D, T]) Count(q *Query[T]) (int64, error)
Count 统计数量
func (*Repository[D, T]) CreateBatch ¶
func (r *Repository[D, T]) CreateBatch(ctx context.Context, entities []*T, batchSize int) error
CreateBatch 批量插入(分批执行,适合大批量数据)。 batchSize 控制每批插入的记录数,防止超出数据库单次 SQL 参数限制。
func (*Repository[D, T]) CreateBatchTx ¶
func (r *Repository[D, T]) CreateBatchTx(ctx context.Context, entities []*T, batchSize int, tx *gorm.DB) error
CreateBatchTx 事务批量插入(分批执行,适合大批量数据)。
func (*Repository[D, T]) DecrByTx ¶ added in v0.3.0
func (r *Repository[D, T]) DecrByTx(u *Updater[T], col any, delta int64, tx *gorm.DB) (int64, error)
DecrByTx 支持事务的原子自减。
func (*Repository[D, T]) DeleteByCond ¶
func (r *Repository[D, T]) DeleteByCond(q *Query[T]) (int64, error)
DeleteByCond 根据条件删除
func (*Repository[D, T]) DeleteByCondTx ¶ added in v0.2.0
DeleteByCondTx 事务根据条件删除
func (*Repository[D, T]) DeleteById ¶
func (r *Repository[D, T]) DeleteById(ctx context.Context, id D) (int64, error)
DeleteById 根据 ID 删除
func (*Repository[D, T]) DeleteByIdTx ¶
DeleteByIdTx 事务删除。 若 ctx 中携带 DataRule,仅当记录满足权限条件时才删除;跨租户 ID 返回 affected=0。
func (*Repository[D, T]) DeleteByIds ¶ added in v0.3.0
func (r *Repository[D, T]) DeleteByIds(ctx context.Context, ids []D) (int64, error)
DeleteByIds 批量按主键删除,ids 为空时直接返回 0,不发 SQL
func (*Repository[D, T]) DeleteByIdsTx ¶ added in v0.3.0
DeleteByIdsTx 支持事务的批量主键删除,ids 为空时直接返回 0,不发 SQL。 若 ctx 中携带 DataRule,仅删除满足权限条件的记录;跨租户 ID 被静默跳过。
func (*Repository[D, T]) Exists ¶ added in v0.3.0
func (r *Repository[D, T]) Exists(q *Query[T]) (bool, error)
Exists 检查是否存在满足条件的记录。 若 q 不含任何条件,等价于检查表中是否存在任何记录(全表 LIMIT 1)。
func (*Repository[D, T]) FirstOrCreate ¶ added in v0.3.0
func (r *Repository[D, T]) FirstOrCreate(q *Query[T], defaults *T) (data T, created bool, err error)
FirstOrCreate 按条件查找记录,不存在时用 defaults 创建。 返回值:(record, created, error),created=true 表示本次新建。 defaults 不可为 nil,否则返回 ErrDefaultsNil。 内部使用事务保证查询与创建的原子性。
func (*Repository[D, T]) FirstOrUpdate ¶ added in v0.3.0
func (r *Repository[D, T]) FirstOrUpdate(q *Query[T], u *Updater[T], defaults *T) (data T, created bool, err error)
FirstOrUpdate 按条件查找记录,找到则执行 Updater 更新,未找到则用 defaults 创建。 返回值:(record, created, error),created=true 表示本次新建。 defaults 不可为 nil,否则返回 ErrDefaultsNil。 内部使用事务保证查找与更新/创建的原子性。
func (*Repository[D, T]) GetById ¶
func (r *Repository[D, T]) GetById(ctx context.Context, id D) (T, error)
GetById 根据主键查询
func (*Repository[D, T]) GetByIdTx ¶
GetByIdTx 支持事务的查询。 若 ctx 中携带 DataRule,将在 WHERE 中追加对应条件;跨租户 ID 返回 gorm.ErrRecordNotFound。
func (*Repository[D, T]) GetByIds ¶ added in v0.3.0
func (r *Repository[D, T]) GetByIds(ctx context.Context, ids []D) ([]T, error)
GetByIds 批量按主键查询,ids 为空时直接返回空切片
func (*Repository[D, T]) GetByIdsTx ¶ added in v0.3.0
GetByIdsTx 支持事务的批量主键查询,ids 为空时直接返回空切片。 若 ctx 中携带 DataRule,结果集仅包含满足权限条件的记录;跨租户 ID 被静默过滤。
func (*Repository[D, T]) GetByLock ¶
func (r *Repository[D, T]) GetByLock(q *Query[T], tx *gorm.DB) (*T, error)
GetByLock 专门的带锁查询方法 强制要求传入 tx,因为不在事务里的锁是没有意义的
func (*Repository[D, T]) GetDB ¶
func (r *Repository[D, T]) GetDB() *gorm.DB
GetDB 获取当前 Repository 绑定的 DB 实例 注意:请勿通过此方法修改 db.Config 或关闭连接
func (*Repository[D, T]) GetOne ¶
func (r *Repository[D, T]) GetOne(q *Query[T]) (data T, err error)
GetOne 根据条件查询单条
func (*Repository[D, T]) GetOneTx ¶
func (r *Repository[D, T]) GetOneTx(q *Query[T], tx *gorm.DB) (data T, err error)
GetOneTx 支持事务的单条查询
func (*Repository[D, T]) IncrBy ¶ added in v0.3.0
IncrBy 原子自增指定列。col 须为 NewUpdater 返回的 *T 实例的字段指针。 u 用于指定 WHERE 条件;未设置条件时返回 ErrUpdateNoCondition 以防全表更新。
func (*Repository[D, T]) IncrByTx ¶ added in v0.3.0
func (r *Repository[D, T]) IncrByTx(u *Updater[T], col any, delta int64, tx *gorm.DB) (int64, error)
IncrByTx 支持事务的原子自增。
func (*Repository[D, T]) InsertBatchOnConflict ¶ added in v0.4.0
func (r *Repository[D, T]) InsertBatchOnConflict(ctx context.Context, entities []T, oc OnConflict) error
InsertBatchOnConflict 执行带冲突处理的批量插入。entities 为空时直接返回,不发 SQL。
func (*Repository[D, T]) InsertBatchOnConflictTx ¶ added in v0.4.0
func (r *Repository[D, T]) InsertBatchOnConflictTx(ctx context.Context, entities []T, oc OnConflict, tx *gorm.DB) error
InsertBatchOnConflictTx 支持事务的批量冲突插入。
func (*Repository[D, T]) InsertOnConflict ¶ added in v0.4.0
func (r *Repository[D, T]) InsertOnConflict(ctx context.Context, entity *T, oc OnConflict) error
InsertOnConflict 执行带冲突处理的单条插入。 根据 OnConflict 策略决定:冲突时跳过(DoNothing)、覆盖指定列(DoUpdates)、覆盖所有列(DoUpdateAll)、原子表达式更新(UpdateExprs)。
func (*Repository[D, T]) InsertOnConflictTx ¶ added in v0.4.0
func (r *Repository[D, T]) InsertOnConflictTx(ctx context.Context, entity *T, oc OnConflict, tx *gorm.DB) error
InsertOnConflictTx 支持事务的单条冲突插入。
func (*Repository[D, T]) Last ¶ added in v0.3.0
func (r *Repository[D, T]) Last(q *Query[T]) (data T, err error)
Last 按主键倒序取第一条记录,语义与 GetOne 对称(GetOne 用 First)。
func (*Repository[D, T]) LastTx ¶ added in v0.3.0
func (r *Repository[D, T]) LastTx(q *Query[T], tx *gorm.DB) (data T, err error)
LastTx 支持事务的 Last。
func (*Repository[D, T]) List ¶
func (r *Repository[D, T]) List(q *Query[T]) (data []T, err error)
List 根据条件查询列表
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/glebarez/sqlite"
"github.com/yi-nanping/gplus"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Article 示例模型
type Article struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Title string `gorm:"column:title"`
Author string `gorm:"column:author"`
Views int `gorm:"column:views"`
Deleted gorm.DeletedAt
}
func openExampleDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
log.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&Article{}); err != nil {
log.Fatalf("migrate: %v", err)
}
return db
}
func main() {
db := openExampleDB()
repo := gplus.NewRepository[uint, Article](db)
ctx := context.Background()
db.Create(&Article{Title: "Go generics", Author: "Alice", Views: 200})
db.Create(&Article{Title: "GORM tips", Author: "Bob", Views: 50})
db.Create(&Article{Title: "gplus guide", Author: "Alice", Views: 300})
q, m := gplus.NewQuery[Article](ctx)
q.Eq(&m.Author, "Alice").Ge(&m.Views, 100).Order(&m.Views, false)
articles, err := repo.List(q)
if err != nil {
log.Fatal(err)
}
for _, a := range articles {
fmt.Printf("%s (%d)\n", a.Title, a.Views)
}
}
Output: gplus guide (300) Go generics (200)
func (*Repository[D, T]) ListMap ¶ added in v0.3.0
func (r *Repository[D, T]) ListMap(q *Query[T], keyFn func(T) D) (map[D]T, error)
ListMap 查询列表并按 keyFn 转换为 map。重复 key 时后者覆盖前者。
func (*Repository[D, T]) ListMapTx ¶ added in v0.3.0
func (r *Repository[D, T]) ListMapTx(q *Query[T], keyFn func(T) D, tx *gorm.DB) (map[D]T, error)
ListMapTx 支持事务的 ListMap。
func (*Repository[D, T]) ListTx ¶
func (r *Repository[D, T]) ListTx(q *Query[T], tx *gorm.DB) (data []T, err error)
ListTx 支持事务的列表查询
func (*Repository[D, T]) NewQuery ¶ added in v0.3.1
func (r *Repository[D, T]) NewQuery(ctx context.Context) (*Query[T], *T)
NewQuery 创建与当前 Repository 同类型的查询构建器,无需重复指定泛型参数。 等价于 gplus.NewQuery[T](ctx),但类型由 Repository 自动推导。
func (*Repository[D, T]) NewQueryAs ¶ added in v0.8.0
func (r *Repository[D, T]) NewQueryAs(ctx context.Context, alias string) (*Query[T], *T)
NewQueryAs 创建带主表 alias 的 Query,便捷方法。 等价于 gplus.NewQueryAs[T](ctx, alias),但类型由 Repository 自动推导。
func (*Repository[D, T]) NewUpdater ¶ added in v0.3.1
func (r *Repository[D, T]) NewUpdater(ctx context.Context) (*Updater[T], *T)
NewUpdater 创建与当前 Repository 同类型的更新构建器,无需重复指定泛型参数。 等价于 gplus.NewUpdater[T](ctx),但类型由 Repository 自动推导。
func (*Repository[D, T]) Page ¶
func (r *Repository[D, T]) Page(q *Query[T], skipCount bool) (data []T, total int64, err error)
Page 分页查询。 skipCount=true 时跳过 COUNT 查询,total 恒为 0,适合已知总数或不需要总数的场景(性能更优)。 skipCount=false 时先执行 COUNT,若总数为 0 则直接返回,不再执行 Find。
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/glebarez/sqlite"
"github.com/yi-nanping/gplus"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Article 示例模型
type Article struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Title string `gorm:"column:title"`
Author string `gorm:"column:author"`
Views int `gorm:"column:views"`
Deleted gorm.DeletedAt
}
func openExampleDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
log.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&Article{}); err != nil {
log.Fatalf("migrate: %v", err)
}
return db
}
func main() {
db := openExampleDB()
repo := gplus.NewRepository[uint, Article](db)
ctx := context.Background()
for i := 1; i <= 5; i++ {
db.Create(&Article{Title: fmt.Sprintf("Article %d", i), Author: "Alice", Views: i * 10})
}
q, _ := gplus.NewQuery[Article](ctx)
q.Limit(3).Offset(0)
list, total, err := repo.Page(q, false)
if err != nil {
log.Fatal(err)
}
fmt.Printf("total=%d page=%d\n", total, len(list))
}
Output: total=5 page=3
func (*Repository[D, T]) PageTx ¶
func (r *Repository[D, T]) PageTx(q *Query[T], skipCount bool, tx *gorm.DB) (data []T, total int64, err error)
PageTx 支持事务的分页查询。skipCount 语义同 Page。
func (*Repository[D, T]) RawExecTx ¶
func (r *Repository[D, T]) RawExecTx(ctx context.Context, tx *gorm.DB, sql string, args ...any) (int64, error)
RawExecTx 在事务中执行原生 SQL(如 INSERT, UPDATE, DELETE 或 DDL 语句) 返回受影响的行数
func (*Repository[D, T]) RawQuery ¶
RawQuery 执行原生查询 SQL,并将结果映射到当前 Repository 的实体切片中 适用场景:复杂的 JOIN 查询或存储过程
func (*Repository[D, T]) RawQueryTx ¶
func (r *Repository[D, T]) RawQueryTx(ctx context.Context, tx *gorm.DB, sql string, args ...any) ([]T, error)
RawQueryTx 在事务中执行原生查询 SQL,并将结果映射到当前 Repository 的实体切片中
func (*Repository[D, T]) RawScan ¶
RawScan 执行原生 SQL 并将结果映射到【任意】指定的结构体或变量中 适用场景:聚合查询(如 SUM/COUNT)或统计类报表
func (*Repository[D, T]) RawScanTx ¶
func (r *Repository[D, T]) RawScanTx(ctx context.Context, tx *gorm.DB, dest any, sql string, args ...any) error
RawScanTx 在事务中执行原生 SQL 并将结果映射到【任意】指定的结构体或变量中
func (*Repository[D, T]) Restore ¶ added in v0.3.0
func (r *Repository[D, T]) Restore(ctx context.Context, id D) (int64, error)
Restore 恢复软删除记录(将 deleted_at 置 NULL)。 返回受影响的行数:1 表示成功恢复,0 表示记录不存在或未被软删除。 注意:模型须包含 gorm.DeletedAt 字段,否则行为未定义。
func (*Repository[D, T]) RestoreByCond ¶ added in v0.3.0
func (r *Repository[D, T]) RestoreByCond(q *Query[T]) (int64, error)
RestoreByCond 按条件批量恢复软删除记录(将 deleted_at 置 NULL)。 空条件会返回 ErrRestoreEmpty,防止意外全表恢复。 返回受影响的行数。
func (*Repository[D, T]) RestoreByCondTx ¶ added in v0.3.0
RestoreByCondTx 支持事务的按条件批量恢复软删除。
func (*Repository[D, T]) RestoreTx ¶ added in v0.3.0
RestoreTx 支持事务的软删除恢复。 注意:启用 DataRule 时,跨租户记录返回 affected==0(不会恢复)。
func (*Repository[D, T]) Save ¶
func (r *Repository[D, T]) Save(ctx context.Context, entity *T) error
Save 纯 INSERT(非 upsert)。 警告:无论 entity 是否携带主键,均执行 INSERT,不会更新已有记录。 若需 insert-or-update 语义,请使用 Upsert。
func (*Repository[D, T]) SaveBatch ¶
func (r *Repository[D, T]) SaveBatch(ctx context.Context, entities []T) error
SaveBatch 批量纯 INSERT(一次性,适合小批量数据)。 警告:底层调用 GORM Create,执行纯插入而非 upsert。 大批量数据请使用 CreateBatch 以控制每批插入数量。
func (*Repository[D, T]) SaveBatchTx ¶
SaveBatchTx 事务批量纯 INSERT(一次性,适合小批量数据)。
func (*Repository[D, T]) ToCountSQL ¶ added in v0.4.0
func (r *Repository[D, T]) ToCountSQL(q *Query[T]) (string, error)
ToCountSQL 将查询转换为 COUNT SQL 字符串,使用 Repository 内部的 DB,不执行实际查询。
func (*Repository[D, T]) ToSQL ¶ added in v0.4.0
func (r *Repository[D, T]) ToSQL(q *Query[T]) (string, error)
ToSQL 将查询转换为 SELECT SQL 字符串,使用 Repository 内部的 DB,不执行实际查询。
Example ¶
package main
import (
"context"
"fmt"
"log"
"github.com/glebarez/sqlite"
"github.com/yi-nanping/gplus"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// Article 示例模型
type Article struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Title string `gorm:"column:title"`
Author string `gorm:"column:author"`
Views int `gorm:"column:views"`
Deleted gorm.DeletedAt
}
func openExampleDB() *gorm.DB {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Discard,
})
if err != nil {
log.Fatalf("open db: %v", err)
}
if err := db.AutoMigrate(&Article{}); err != nil {
log.Fatalf("migrate: %v", err)
}
return db
}
func main() {
db := openExampleDB()
repo := gplus.NewRepository[uint, Article](db)
ctx := context.Background()
q, m := gplus.NewQuery[Article](ctx)
q.Eq(&m.Author, "Alice").Gt(&m.Views, 100)
sql, err := repo.ToSQL(q)
if err != nil {
log.Fatal(err)
}
fmt.Println(sql != "")
}
Output: true
func (*Repository[D, T]) ToUpdateSQL ¶ added in v0.4.0
func (r *Repository[D, T]) ToUpdateSQL(u *Updater[T]) (string, error)
ToUpdateSQL 将更新操作转换为 UPDATE SQL 字符串,使用 Repository 内部的 DB,不执行实际查询。
func (*Repository[D, T]) Transaction ¶
Transaction 封装 GORM 的事务闭包模式 fn: 事务内的业务逻辑,如果返回 error,事务会自动回滚
func (*Repository[D, T]) UpdateByCond ¶
func (r *Repository[D, T]) UpdateByCond(u *Updater[T]) (int64, error)
UpdateByCond 执行条件更新(不带事务)
func (*Repository[D, T]) UpdateByCondTx ¶ added in v0.2.0
UpdateByCondTx 执行条件更新(支持事务)
func (*Repository[D, T]) UpdateById ¶
func (r *Repository[D, T]) UpdateById(ctx context.Context, entity *T) error
UpdateById 根据 ID 更新
func (*Repository[D, T]) UpdateByIdTx ¶
UpdateByIdTx 事务更新。若模型含 `gplus:"version"` 字段则启用乐观锁: WHERE id=? AND version=oldVer,SET ..., version=version+1; affected==0 时返回 ErrOptimisticLock(版本冲突或记录不存在)。 成功后 entity.Version 自动递增,可直接再次调用。
注意:启用 DataRule 时,记录存在但跨租户会返回 affected==0(返回 ErrOptimisticLock), 此时不应无条件重试(重试无法绕过权限)。乐观锁版本冲突与 DataRule 拦截当前共用同一错误码, 调用方需通过其他途径区分(如先 GetById 检查记录是否在权限范围内)。
func (*Repository[D, T]) UpdateByIds ¶ added in v0.3.0
UpdateByIds 批量按主键更新,ids 为空时直接返回 0,不发 SQL
func (*Repository[D, T]) UpdateByIdsTx ¶ added in v0.3.0
func (r *Repository[D, T]) UpdateByIdsTx(ctx context.Context, ids []D, u *Updater[T], tx *gorm.DB) (int64, error)
UpdateByIdsTx 支持事务的批量主键更新。
注意:启用 DataRule 时,记录存在但跨租户会返回 affected==0,此时不应无条件重试 (重试无法绕过权限)。调用方需通过其他途径区分权限拦截与实际无匹配行。
func (*Repository[D, T]) Upsert ¶ added in v0.2.0
func (r *Repository[D, T]) Upsert(ctx context.Context, entity *T) error
Upsert 保存或更新单条记录(insert-or-update)。 底层调用 GORM db.Save():有主键时执行 UPDATE 全字段,无主键时执行 INSERT。 注意:UPDATE 会覆盖所有字段(包括零值),如需只更新部分字段请使用 UpdateById/UpdateByCond。
func (*Repository[D, T]) UpsertBatch ¶ added in v0.2.0
func (r *Repository[D, T]) UpsertBatch(ctx context.Context, entities []T) error
UpsertBatch 批量保存或更新(insert-or-update,一次性执行)。 底层调用 GORM db.Save(),每条记录按主键决定 INSERT 或 UPDATE。
func (*Repository[D, T]) UpsertBatchTx ¶ added in v0.2.0
UpsertBatchTx 事务批量保存或更新(insert-or-update)。
func (*Repository[D, T]) WithTx ¶
func (r *Repository[D, T]) WithTx(tx *gorm.DB) *Repository[D, T]
WithTx 返回一个新的 Repository 实例,该实例绑定了传入的事务对象 这是一个轻量级的浅拷贝,性能消耗极小
type ScopeBuilder ¶
type ScopeBuilder struct {
// contains filtered or unexported fields
}
ScopeBuilder 负责将条件转换为 GORM Scope 这是 QueryCond 和 UpdateCond 的基类
func (*ScopeBuilder) BuildCount ¶
func (b *ScopeBuilder) BuildCount() func(*gorm.DB) *gorm.DB
BuildCount 计数构建
func (*ScopeBuilder) BuildDelete ¶
func (b *ScopeBuilder) BuildDelete() func(*gorm.DB) *gorm.DB
BuildDelete 专门用于删除 (Delete) 删除操作最核心的是 Where 和 Unscoped
func (*ScopeBuilder) BuildQuery ¶
func (b *ScopeBuilder) BuildQuery() func(*gorm.DB) *gorm.DB
BuildQuery 专门用于查询 (Find/First/List)
func (*ScopeBuilder) BuildUpdate ¶
func (b *ScopeBuilder) BuildUpdate() func(*gorm.DB) *gorm.DB
BuildUpdate 专门用于更新 (Updates/Update/Save) 更新逻辑不应包含 Distinct, Limit, Offset, Order
type Subquerier ¶ added in v0.6.0
type Subquerier interface {
// ToDB 返回可作为 GORM 子查询绑定参数的 *gorm.DB 对象
ToDB(db *gorm.DB) *gorm.DB
// GetError 返回构建过程累积的错误
GetError() error
// contains filtered or unexported methods
}
Subquerier 子查询契约。任意 *Query[X] 自动满足(X 可与外层 T 不同)。 gplusSubquery() 私有方法限制接口只能由本包实现。
type Updater ¶
type Updater[T any] struct { // ScopeBuilder 是 Updater 的核心,用于构建 SQL 语句 ScopeBuilder // contains filtered or unexported fields }
func NewUpdater ¶
NewUpdater 创建泛型更新构建器,同时返回类型 T 的规范实例指针。 所有字段指针参数(如 &model.Name)必须来自返回的 *T 实例。 ctx 用于传递请求级上下文,可传 context.Background()。
func (*Updater[T]) And ¶
And 开启一个带括号的 AND 嵌套块
示例:u.Eq(&User.Status, 1).And(func(sub *gplus.Updater[User]) {
sub.Gt(&User.Age, 18).OrEq(&User.IsVip, true)
})
func (*Updater[T]) Clear ¶
func (u *Updater[T]) Clear()
Clear 重写 Updater 的清除逻辑 v0.8.0 N4:翻转所有 alias entry 的 revoked 标志,防 Clear 后用 alias 残骸。 保留 entries(不清空 aliases map),让后续 lookupAddr 命中 revoked 拦截。
func (*Updater[T]) DataRuleBuilder ¶ added in v0.2.0
DataRuleBuilder 从上下文中提取规则并应用到更新条件中。 对同一个 Updater 对象只执行一次,防止多次调用重复追加条件。
func (*Updater[T]) EqSub ¶ added in v0.6.0
func (u *Updater[T]) EqSub(col any, sub Subquerier) *Updater[T]
EqSub = 子查询。
func (*Updater[T]) Exists ¶ added in v0.8.0
func (u *Updater[T]) Exists(sub Subquerier) *Updater[T]
Exists 添加 EXISTS 子查询条件(AND)。 等价于 WHERE EXISTS (subquery)。 若 sub 为 nil 或子查询有错误,错误会累积到 Updater,可通过 GetError() 获取。
func (*Updater[T]) GtSub ¶ added in v0.6.0
func (u *Updater[T]) GtSub(col any, sub Subquerier) *Updater[T]
GtSub > 子查询。
func (*Updater[T]) GteSub ¶ added in v0.6.0
func (u *Updater[T]) GteSub(col any, sub Subquerier) *Updater[T]
GteSub >= 子查询。
func (*Updater[T]) InSub ¶ added in v0.6.0
func (u *Updater[T]) InSub(col any, sub Subquerier) *Updater[T]
InSub IN 子查询:col IN (subquery)。 sub 须为本包创建的 *Query[X](满足 Subquerier 接口)。 参见 Query.InSub。
func (*Updater[T]) InnerJoinAs ¶ added in v0.8.0
func (u *Updater[T]) InnerJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Updater[T]
InnerJoinAs Updater 类型安全的 INNER JOIN(v0.8.0 alias 体系,M2 精简)。
func (*Updater[T]) LeftJoinAs ¶ added in v0.8.0
func (u *Updater[T]) LeftJoinAs(alias any, leftCol any, rightCol any, extraSQL string, extraArgs ...any) *Updater[T]
LeftJoinAs Updater 类型安全的 LEFT JOIN(v0.8.0 alias 体系,M2 精简)。
alias 必须由 As[X](u, ...) 创建的实例,且属于当前 u 链; leftCol / rightCol 为任意一侧的字段指针; extraSQL 为额外的 ON 条件 SQL 片段(含 ? 占位符),extraArgs 对应参数。
func (*Updater[T]) LtSub ¶ added in v0.6.0
func (u *Updater[T]) LtSub(col any, sub Subquerier) *Updater[T]
LtSub < 子查询。
func (*Updater[T]) LteSub ¶ added in v0.6.0
func (u *Updater[T]) LteSub(col any, sub Subquerier) *Updater[T]
LteSub <= 子查询。
func (*Updater[T]) NeSub ¶ added in v0.6.0
func (u *Updater[T]) NeSub(col any, sub Subquerier) *Updater[T]
NeSub <> 子查询。
func (*Updater[T]) NotBetween ¶
NotBetween 区间查询
func (*Updater[T]) NotExists ¶ added in v0.8.0
func (u *Updater[T]) NotExists(sub Subquerier) *Updater[T]
NotExists 添加 NOT EXISTS 子查询条件(AND)。详见 Exists。
func (*Updater[T]) NotInSub ¶ added in v0.6.0
func (u *Updater[T]) NotInSub(col any, sub Subquerier) *Updater[T]
NotInSub NOT IN 子查询。
注意 MySQL ERROR 1093:UPDATE 同表 IN 子查询限制(如
UPDATE users WHERE id IN (SELECT id FROM users WHERE x)
报错)。可改写为 JOIN UPDATE 或子查询包一层临时表。
func (*Updater[T]) OrEqSub ¶ added in v0.6.0
func (u *Updater[T]) OrEqSub(col any, sub Subquerier) *Updater[T]
OrEqSub = 子查询(或)。
func (*Updater[T]) OrExists ¶ added in v0.8.0
func (u *Updater[T]) OrExists(sub Subquerier) *Updater[T]
OrExists 添加 OR EXISTS 子查询条件。与 Exists 相同,但使用 OR 逻辑。
func (*Updater[T]) OrGtSub ¶ added in v0.6.0
func (u *Updater[T]) OrGtSub(col any, sub Subquerier) *Updater[T]
OrGtSub > 子查询(或)。
func (*Updater[T]) OrGteSub ¶ added in v0.6.0
func (u *Updater[T]) OrGteSub(col any, sub Subquerier) *Updater[T]
OrGteSub >= 子查询(或)。
func (*Updater[T]) OrInSub ¶ added in v0.6.0
func (u *Updater[T]) OrInSub(col any, sub Subquerier) *Updater[T]
OrInSub IN 子查询(或)。
func (*Updater[T]) OrIsNotNull ¶
OrIsNotNull 不为空
func (*Updater[T]) OrLikeLeft ¶
OrLikeLeft 左模糊查询(或)
func (*Updater[T]) OrLikeRight ¶
OrLikeRight 右模糊查询(或)
func (*Updater[T]) OrLtSub ¶ added in v0.6.0
func (u *Updater[T]) OrLtSub(col any, sub Subquerier) *Updater[T]
OrLtSub < 子查询(或)。
func (*Updater[T]) OrLteSub ¶ added in v0.6.0
func (u *Updater[T]) OrLteSub(col any, sub Subquerier) *Updater[T]
OrLteSub <= 子查询(或)。
func (*Updater[T]) OrNeSub ¶ added in v0.6.0
func (u *Updater[T]) OrNeSub(col any, sub Subquerier) *Updater[T]
OrNeSub <> 子查询(或)。
func (*Updater[T]) OrNotBetween ¶
OrNotBetween 区间查询
func (*Updater[T]) OrNotExists ¶ added in v0.8.0
func (u *Updater[T]) OrNotExists(sub Subquerier) *Updater[T]
OrNotExists 添加 OR NOT EXISTS 子查询条件。与 NotExists 相同,但使用 OR 逻辑。
func (*Updater[T]) OrNotInSub ¶ added in v0.6.0
func (u *Updater[T]) OrNotInSub(col any, sub Subquerier) *Updater[T]
OrNotInSub NOT IN 子查询(或)。
func (*Updater[T]) OrWhereRaw ¶ added in v0.2.0
OrWhereRaw 添加原生 SQL 条件(OR)。 参数安全要求与 WhereRaw 相同。
func (*Updater[T]) SetExpr ¶
SetExpr 设置 SQL 表达式更新 (原子更新) 示例: u.SetExpr(&User.Age, "age + ?", 1) -> UPDATE ... SET age = age + 1
func (*Updater[T]) SetMap ¶
SetMap 批量设置更新内容 注意:map 的 key 必须是数据库列名(snake_case,如 "user_name"), 而非结构体字段名(如 "UserName")。如需类型安全的列名解析,请改用 Set()。
func (*Updater[T]) ToSQL ¶ added in v0.4.0
ToSQL 将当前更新操作转换为 UPDATE SQL 字符串,不执行实际更新。 返回的 SQL 已将参数内联,仅用于调试展示。
func (*Updater[T]) UpdateMap ¶
UpdateMap 获取最终的更新 Map 的只读副本 返回副本而非原始引用,防止调用方绕过 Set/SetMap 的列名校验直接修改内部状态
func (*Updater[T]) WhereRaw ¶ added in v0.2.0
WhereRaw 添加原生 SQL 条件(AND)。 sql 为完整条件片段,args 为参数绑定值,防止 SQL 注入。 示例:u.WhereRaw("YEAR(created_at) = ?", 2024) 注意:sql 参数由调用方负责安全性,不可直接拼接用户输入。