说人话的 GORM文档 之 链式调用

43 min read

什么是链式调用

链式调用(也称为链式方法)是一种编程风格,它允许你通过将多个方法调用链接在一起来执行一系列操作。在 GORM 中,链式调用通常被用来构建查询条件。

例如,下面是一个使用链式调用来构建查询条件的例子:

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

在上面的例子中,我们使用了两个 Where 方法来构建查询条件。第一个 Where 方法用于指定 name 字段的值,第二个 Where 方法用于指定 age 字段的值。最后,我们调用了 First 方法来执行查询。

链式调用的优点在于它可以让你的代码更加简洁易读。你可以通过将多个方法调用链接在一起来清晰地表达你的意图。

链式调用的坑

在 GORM 中,当你使用链式方法(例如 WhereSelectOmit 等)时,它们会返回一个新的 *gorm.DB 实例。这个新实例包含了之前调用链式方法时设置的条件。如果你再次使用这个实例调用链式方法,那么新的条件会被添加到之前的条件中

这意味着,如果你重复使用同一个 *gorm.DB 实例,那么新生成的 SQL 可能会被先前的条件污染。因此,我们不建议重复使用同一个 *gorm.DB 实例。

例如:

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

在上面的例子中,我们重复使用了 queryDB 这个 *gorm.DB 实例。因此,在第二次调用 Where 方法时,新的条件 age > 20 被添加到了之前的条件中。

为了避免这种情况,你可以使用 Session 方法来创建一个新的 *gorm.DB 实例,然后在这个新实例上调用链式方法。例如:

queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{})
queryDB.Where("age > ?", 10).First(&user) // SELECT * FROM users WHERE name = "jinzhu" AND age > 10
queryDB.Where("age > ?", 20).First(&user2) // SELECT * FROM users WHERE name = "jinzhu" AND age > 20

在上面的例子中,我们使用了 Session 方法来创建一个新的 *gorm.DB 实例。因此,在第二次调用 Where 方法时,新的条件 age > 20 没有被添加到之前的条件中。和new bing一样,每次都开启了都清除了记忆并开启了一个新的会话

可共享的 *gorm.DB

可共享的 *gorm.DB 是指一个可以安全地重复使用的 *gorm.DB 实例。在 GORM 中,您可以使用新建会话方法来创建一个可共享的 *gorm.DB 实例。例如:

queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{})
queryDB.Where("age > ?", 10).First(&user) // SELECT * FROM users WHERE name = "jinzhu" AND age > 10
queryDB.Where("age > ?", 20).First(&user2) // SELECT * FROM users WHERE name = "jinzhu" AND age > 20

在这个示例中,我们使用了 Session 方法来创建一个可共享的 *gorm.DB 实例。您也可以使用 WithContext 或 Debug 方法来创建可共享的实例。

哪些方法后不能使用链式调用?

在 GORM 中,当你调用 Finisher 方法(例如 CreateFirstFindTakeSaveUpdateDeleteScanRowRows 等)时,它们会立即执行注册回调,然后生成并执行 SQL。这意味着,在调用 Finisher 方法之后,你不能再使用链式方法来修改查询条件。

例如:

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

在上面的例子中,我们在调用 First 方法之后又调用了 Where 方法。但是,这样做是无效的,因为 First 方法是一个 Finisher 方法,它会立即执行注册回调并生成 SQL。

如果你想在调用 Finisher 方法之后继续使用链式方法,那么你应该使用 Session 方法来创建一个新的 *gorm.DB 实例,然后在这个新实例上调用链式方法。例如:

db.Where("name = ?", "jinzhu").First(&user)
db.Session(&gorm.Session{}).Where("age > ?", 10).First(&user2)

// 对应执行的SQL语句
// SELECT * FROM users WHERE name = "jinzhu" LIMIT 1
// SELECT * FROM users WHERE age > 10 LIMIT 1

在上面的例子中,我们使用了 Session 方法来创建一个新的 *gorm.DB 实例。因此,在第二次调用 Where 方法时,新的条件 age > 10 没有被添加到之前的条件中。

如何终结链式调用

在 GORM 中,当你调用 Finisher 方法(例如 CreateFirstFindTakeSaveUpdateDeleteScanRowRows 等)时,它们会立即执行注册回调,然后生成并执行 SQL。这意味着,在调用 Finisher 方法之后,你不能再使用链式方法来修改查询条件。