方法链

GORM 的方法链功能允许流畅且易读的编码风格。以下是一个示例

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

方法分类

GORM 将方法分为三大类:链式方法结束方法新会话方法

链式方法

链式方法用于修改或追加 子句 到当前的 Statement。一些常见的链式方法包括

  • Where
  • Select
  • Omit
  • Joins
  • Scopes
  • Preload
  • Raw(注意:Raw 不能与其他可链接方法一起使用来构建 SQL)

有关完整列表,请访问 GORM 可链接 API。此外,SQL 构建器 文档提供了有关 子句 的更多详细信息。

结束方法

结束方法是立即执行的,执行已注册的回调以生成并运行 SQL 命令。此类别包括以下方法

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

有关完整列表,请参阅 GORM 结束方法 API

新会话方法

GORM 将 SessionWithContextDebug 等方法定义为新会话方法,这些方法对于创建可共享和可重用的 *gorm.DB 实例至关重要。有关更多详细信息,请参阅 会话 文档。

可重用性和安全性

GORM 的一个关键方面是了解何时可以安全地重用 *gorm.DB 实例。在 链式方法结束方法 之后,GORM 返回一个已初始化的 *gorm.DB 实例。此实例不安全,因为它可能携带先前操作的条件,从而可能导致 SQL 查询受到污染。例如

不安全重用示例

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

// First query
queryDB.Where("age > ?", 10).First(&user)
// SQL: SELECT * FROM users WHERE name = "jinzhu" AND age > 10

// Second query with unintended compounded condition
queryDB.Where("age > ?", 20).First(&user2)
// SQL: SELECT * FROM users WHERE name = "jinzhu" AND age > 10 AND age > 20

安全重用示例

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

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

// First query
queryDB.Where("age > ?", 10).First(&user)
// SQL: SELECT * FROM users WHERE name = "jinzhu" AND age > 10

// Second query, safely isolated
queryDB.Where("age > ?", 20).First(&user2)
// SQL: SELECT * FROM users WHERE name = "jinzhu" AND age > 20

在这种情况下,使用 Session(&gorm.Session{}) 可确保每个查询都从一个全新的上下文开始,防止 SQL 查询受到先前操作条件的污染。这对于维护数据库交互的完整性和准确性至关重要。

清晰示例

让我们通过几个例子来澄清

  • 示例 1:安全实例重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
// 'db' is a newly initialized `*gorm.DB`, which is safe to reuse.

db.Where("name = ?", "jinzhu").Where("age = ?", 18).Find(&users)
// The first `Where("name = ?", "jinzhu")` call is a chain method that initializes a `*gorm.DB` instance, or `*gorm.Statement`.
// The second `Where("age = ?", 18)` call adds a new condition to the existing `*gorm.Statement`.
// `Find(&users)` is a finisher method, executing registered Query Callbacks, generating and running:
// SELECT * FROM users WHERE name = 'jinzhu' AND age = 18;

db.Where("name = ?", "jinzhu2").Where("age = ?", 20).Find(&users)
// Here, `Where("name = ?", "jinzhu2")` starts a new chain, creating a fresh `*gorm.Statement`.
// `Where("age = ?", 20)` adds to this new statement.
// `Find(&users)` again finalizes the query, executing and generating:
// SELECT * FROM users WHERE name = 'jinzhu2' AND age = 20;

db.Find(&users)
// Directly calling `Find(&users)` without any `Where` starts a new chain and executes:
// SELECT * FROM users;

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

  • (错误)示例 2:不安全实例重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
// 'db' is a newly initialized *gorm.DB, safe for initial reuse.

tx := db.Where("name = ?", "jinzhu")
// `Where("name = ?", "jinzhu")` initializes a `*gorm.Statement` instance, which should not be reused across different logical operations.

// Good case
tx.Where("age = ?", 18).Find(&users)
// Reuses 'tx' correctly for a single logical operation, executing:
// SELECT * FROM users WHERE name = 'jinzhu' AND age = 18

// Bad case
tx.Where("age = ?", 28).Find(&users)
// Incorrectly reuses 'tx', compounding conditions and leading to a polluted query:
// SELECT * FROM users WHERE name = 'jinzhu' AND age = 18 AND age = 28;

在这个错误的例子中,重用 tx 变量会导致条件复合,这通常是不希望的。

  • 示例 3:使用新会话方法安全重用
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
// 'db' is a newly initialized *gorm.DB, safe to reuse.

tx := db.Where("name = ?", "jinzhu").Session(&gorm.Session{})
tx := db.Where("name = ?", "jinzhu").WithContext(context.Background())
tx := db.Where("name = ?", "jinzhu").Debug()
// `Session`, `WithContext`, `Debug` methods return a `*gorm.DB` instance marked as safe for reuse. They base a newly initialized `*gorm.Statement` on the current conditions.

// Good case
tx.Where("age = ?", 18).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age = 18

// Good case
tx.Where("age = ?", 28).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age = 28;

在此示例中,使用新会话方法 SessionWithContextDebug 正确初始化了每个逻辑操作的 *gorm.DB 实例,防止了条件污染,并确保每个查询都是不同的,并且基于提供的特定条件。

总的来说,这些示例说明了理解 GORM 在方法链和实例管理方面的行为对于确保准确高效的数据库查询的重要性。

铂金赞助商

黄金赞助商

铂金赞助商

黄金赞助商