教程

方法链

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

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

GORM 的方法链特性支持流畅且自然的编码风格。以下是使用传统 API 和泛型 API 的示例:

传统 API

db.Where("name = ?", "jinzhu").Where("age = ?", 18).First(&user)

泛型 API (>= v1.30.0)

ctx := context.Background()
user, err := gorm.G[User](db).Where("name = ?", "jinzhu").Where("age = ?", 18).First(ctx)

两种 API 都支持方法链,但泛型 API 提供了增强的类型安全性,并且直接从操作方法中返回错误。

方法分类

GORM 将方法分为三个主要类别:链式方法、终结方法和新会话方法。这些分类同时适用于传统 API 和泛型 API。

链式方法

链式方法用于修改或向当前 Statement 追加 Clauses。常见的链式方法包括:

  • Where
  • Select
  • Omit
  • Joins
  • Scopes
  • Preload
  • Raw(注意:Raw 不能与其他可链式调用的方法组合使用来构建 SQL)

完整的方法列表请参阅 GORM Chainable API。此外,SQL Builder 文档提供了更多关于 Clauses 的详细信息。

终结方法

终结方法会立即执行注册的回调,生成并运行 SQL 命令。此类方法包括:

  • Create
  • First
  • Find
  • Take
  • Save
  • Update
  • Delete
  • Scan
  • Row
  • Rows

完整的方法列表请参阅 GORM Finisher API。

新会话方法

GORM 将 Session、WithContext 和 Debug 等方法定义为新会话方法,它们对于创建可共享和可重用的 *gorm.DB 实例至关重要。更多详情请参阅 Session 文档。

可重用性与安全性

传统 API

GORM 传统 API 的一个重要方面是理解何时 *gorm.DB 实例可以安全重用。在执行链式方法或终结方法之后,GORM 会返回一个已初始化的 *gorm.DB 实例。该实例不安全重用,因为它可能携带之前操作的条件,从而导致 SQL 查询被污染。例如:

不安全重用的示例

queryDB := DB.Where("name = ?", "jinzhu")


queryDB.Where("age > ?", 10).First(&user)



queryDB.Where("age > ?", 20).First(&user2)

安全重用的示例

要安全地重用 *gorm.DB 实例,请使用新会话方法:

queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{})


queryDB.Where("age > ?", 10).First(&user)



queryDB.Where("age > ?", 20).First(&user2)

在此场景中,使用 Session(&gorm.Session{}) 确保每个查询都从全新的上下文开始,防止之前操作的条件污染 SQL 查询。这对于保持数据库交互的完整性和准确性至关重要。

泛型 API

GORM 泛型 API 的一个显著优势是它天然解决了 SQL 污染问题。使用泛型 API 时,你无需担心实例不安全重用,因为:

  1. 上下文直接传递给每个操作方法
  2. 错误直接从操作方法返回
  3. 泛型接口设计防止了条件污染

以下是一个泛型 API 安全处理方法链的示例:

ctx := context.Background()


genericDB := gorm.G[User](db).Where("name = ?", "jinzhu")


user1, err1 := genericDB.Where("age > ?", 10).First(ctx)



user2, err2 := genericDB.Where("age > ?", 20).First(ctx)

泛型 API 的设计显著降低了 SQL 污染的风险,使你的数据库交互更加可靠和可预测。

示例说明

让我们通过几个同时使用传统 API 和泛型 API 的示例来加以说明:

传统 API 示例

  • 示例 1:安全的实例重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})


db.Where("name = ?", "jinzhu").Where("age = ?", 18).Find(&users)





db.Where("name = ?", "jinzhu2").Where("age = ?", 20).Find(&users)





db.Find(&users)

在此示例中,每个方法调用链都是独立的,确保生成干净、未被污染的 SQL 查询。

  • (反例)示例 2:不安全的实例重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})


tx := db.Where("name = ?", "jinzhu")



tx.Where("age = ?", 18).Find(&users)




tx.Where("age = ?", 28).Find(&users)

在这个反例中,重用 tx 变量会导致条件叠加,这通常是不可取的。

  • 示例 3:使用新会话方法进行安全重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})


tx := db.Where("name = ?", "jinzhu").Session(&gorm.Session{})
tx := db.Where("name = ?", "jinzhu").WithContext(context.Background())
tx := db.Where("name = ?", "jinzhu").Debug()



tx.Where("age = ?", 18).Find(&users)



tx.Where("age = ?", 28).Find(&users)

在此示例中,使用新会话方法 Session、WithContext、Debug 为每个逻辑操作正确初始化了一个 *gorm.DB 实例,防止条件污染,并确保每个查询都是独立的,基于所提供的特定条件执行。

泛型 API 示例

  • 示例 4:使用泛型 API 进行方法链调用
ctx := context.Background()


db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})


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



genericDB := gorm.G[User](db).Where("name = ?", "jinzhu")


user1, err1 := genericDB.Where("age = ?", 18).First(ctx)


users, err2 := genericDB.Where("age > ?", 20).Find(ctx)



users, err3 := gorm.G[User](db).Raw("SELECT * FROM users WHERE name = ? AND age > ?", "jinzhu", 18).Find(ctx)

在此示例中,泛型 API 在保持流畅方法链风格的同时提供了类型安全性。上下文直接传递给终结方法(First、Find),错误直接从这些方法返回,遵循 Go 的标准错误处理模式。

总的来说,这些示例说明了理解 GORM 在方法链和实例管理方面行为的重要性,以确保数据库查询的准确性和高效性。与传统 API 相比,泛型 API 提供了更类型安全、更不易出错的方法链方式。

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…