使用 GORM 的泛型方式
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 年的成长是不可能实现的 ❤️
评论
登录后参与评论
KnowForge