入门指南

使用 GORM 的泛型方式

qianmoQqianmoQ· 更新于 2026-09-23· 阅读 11 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

GORM 在其最新版本(>= v1.30.0)中正式引入了对 Go 泛型 的支持。这一新增功能显著提升了可用性和类型安全性,同时减少了复用 gorm.DB 实例所导致的 SQL 污染等问题。此外,我们还改进了 Joins 和 Preload 的行为,并引入了事务超时处理以防止连接池泄漏。

本次更新以精心设计的方式引入了泛型 API,同时与现有 API 保持完全的向后兼容。你可以在项目中自由混合使用传统 API 和泛型 API——只需在新代码中使用泛型,无需担心与现有逻辑或 GORM 插件(如加密/解密、分片、读写分离、追踪等)的兼容性问题。

为了防止误用,我们有意在泛型版本中移除了某些容易产生歧义或并发问题的 API,例如 FirstOrCreate 和 Save。同时,我们正在设计一款全新的 gorm CLI 工具,未来将提供更强大的代码生成能力、增强的类型安全性以及 lint 支持——进一步降低误用风险。

我们强烈建议在新项目或重构工作中使用新的基于泛型的 API,以享受更好的开发体验、更完善的类型保证和更易于维护的代码库。

泛型 API

GORM 的泛型 API 紧密对应原有 API 的功能。以下是使用新泛型 API 的一些常见操作示例:

ctx := context.Background()


gorm.G[User](db).Create(ctx, &User{Name: "Alice"})
gorm.G[User](db).CreateInBatches(ctx, users, 10)


user, err := gorm.G[User](db).Where("name = ?", "Jinzhu").First(ctx)
users, err := gorm.G[User](db).Where("age <= ?", 18).Find(ctx)


gorm.G[User](db).Where("id = ?", u.ID).Update(ctx, "age", 18)
gorm.G[User](db).Where("id = ?", u.ID).Updates(ctx, User{Name: "Jinzhu", Age: 18})


gorm.G[User](db).Where("id = ?", u.ID).Delete(ctx)

泛型 API 通过接受可选参数(如子句配置或基于插件的选项,例如提示、解析器)来全面支持 GORM 的高级功能,从而实现强大且灵活的行为。


err := gorm.G[Language](DB, clause.OnConflict{DoNothing: true}).Create(ctx, &lang)
err := gorm.G[Language](DB, clause.OnConflict{
  Columns:   []clause.Column{{Name: "id"}},
  DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(ctx, &lang)


err := gorm.G[User](DB,
  hints.New("MAX_EXECUTION_TIME(100)"),
  hints.New("USE_INDEX(t1, idx1)"),
).Find(ctx)



err := gorm.G[User](DB, dbresolver.Write).Find(ctx)


result := gorm.WithResult()
err := gorm.G[User](DB, result).CreateInBatches(ctx, &users, 2)

Joins / Preload 增强

新的 GORM 泛型接口为关联查询(Joins)和预加载(Preload)带来了增强支持,提供了更灵活的关联方式、更强大的查询能力,并显著简化了复杂查询的构建方式。

  • Joins:轻松指定不同的连接类型(如 InnerJoin、LeftJoin),并基于关联自定义连接条件,使复杂的跨表查询更加清晰直观。

users, err := gorm.G[User](db).Joins(clause.Has("Company"), nil).Find(ctx)


user, err = gorm.G[User](db).Joins(clause.LeftJoin.Association("Company"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {
    db.Where(map[string]any{"name": company.Name})
    return nil
}).Where(map[string]any{"name": user.Name}).First(ctx)


users, err = gorm.G[User](db).Joins(clause.LeftJoin.AssociationFrom("Company", gorm.G[Company](DB).Select("Name")).As("t"),
    func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {
        db.Where("?.name = ?", joinTable, u.Company.Name)
        return nil
    },
).Find(ctx)
  • Preload:简化了预加载的条件设置,并引入了 LimitPerRecord 选项,该选项允许在预加载集合时限制每条主记录关联加载的记录数量。

users, err := gorm.G[User](db).Preload("Friends", func(db gorm.PreloadBuilder) error {
    db.Where("age > ?", 14)
    return nil
}).Where("age > ?", 18).Find(ctx)


users, err := gorm.G[User](db).Preload("Friends.Pets", nil).Where("age > ?", 18).Find(ctx)


users, err = gorm.G[User](db).Preload("Friends", func(db gorm.PreloadBuilder) error {
    db.Select("id", "name").Order("age desc")
    return nil
}).Preload("Friends.Pets", func(db gorm.PreloadBuilder) error {
    db.LimitPerRecord(2)
    return nil
}).Find(ctx)

复杂原生 SQL

泛型接口继续支持通过 Raw 执行原生 SQL,以应对复杂或边缘场景:

users, err := gorm.G[User](DB).Raw("SELECT name FROM users WHERE id = ?", user.ID).Find(ctx)

但我们强烈建议使用我们的新代码生成工具来实现类型安全、可维护且安全的原生查询——从而降低语法错误或 SQL 注入等风险。

代码生成器工作流程

  • 1. 安装 CLI 工具:
go install gorm.io/cli/gorm@latest
  • 2. 定义查询接口:

只需使用 Go 的 interface 语法定义查询接口,将 SQL 模板作为注释嵌入:

type Query[T any] interface {
  
  
  
  GetByID(id int) (T, error)

  
  FilterWithColumn(column string, value string) (T, error)

  
  
  
  
  
  
  QueryWith(user models.User) (T, error)

  
  
  
  
  
  
  
  Update(user models.User, id int) error

  
  
  
  
  
  
  
  
  Filter(users []models.User) ([]T, error)

  
  FilterByNameAndAge(name string, age int)

  
  
  
  
  
  
  
  
  
  FilterWithTime(start, end time.Time) ([]T, error)
}
  • 3. 运行生成器:
gorm gen -i ./examples/example.go -o query
  • 4. 使用生成的 API:
import "your_project/query"

company, err := query.Query[Company](db).GetByID(ctx, 10)

user, err := query.Query[User](db).GetByID(ctx, 10)



err := query.Query[User](db).FilterByNameAndAge("jinzhu", 18).Delete(ctx)


users, err := query.Query[User](db).FilterByNameAndAge("jinzhu", 18).Find(ctx)

总结

本次发布标志着 GORM 在泛型支持和全新的 gorm 命令行工具方面迈出了重要一步。这些功能已计划了相当长时间,我们很高兴终于能将初步实现带给社区。

在接下来的更新中,我们将继续完善泛型 API、增强 CLI 工具,并相应更新官方 https://gorm.io 文档——旨在提供更清晰、更高效的开发者体验。

我们由衷感谢多年来所有 GORM 用户和赞助商的支持。没有你们,GORM 过去 12 年的成长是不可能实现的 ❤️

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…