GORM 允许进行链式操作,所以您可以像这样写代码:

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

GORM 中有三种类型的方法: 链式方法Finisher 方法新建会话方法

链式方法

链式方法是将 Clauses 修改或添加到当前 Statement 的方法,例如:

Where, Select, Omit, Joins, Scopes, Preload, Raw

这是 完整方法列表,也可以查看 SQL 构建器 获取更多关于 Clauses 的信息

Finisher Method

Finishers 是会立即执行注册回调的方法,然后生成并执行 SQL,比如这些方法:

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

查看完整方法列表

新建会话模式

在初始化了 *gorm.DB新建会话方法 后, 调用下面的方法会创建一个新的 Statement 实例而不是使用当前的

GROM 定义了 SessionWithContextDebug 方法做为 新建会话方法,参考会话 获得详细

让我们用一些例子来解释它:

示例 1:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // db 是一个刚完成初始化的 *gorm.DB 实例,这是一个 `新建会话`
  3. db.Where("name = ?", "jinzhu").Where("age = ?", 18).Find(&users)
  4. // `Where("name = ?", "jinzhu")` 是调用的第一个方法,它会创建一个新 `Statement`
  5. // `Where("age = ?", 18)` 会复用 `Statement`,并将条件添加至这个 `Statement`
  6. // `Find(&users)` 是一个 finisher 方法,它运行注册的查询回调,生成并运行下面这条 SQL:
  7. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18;
  8. db.Where("name = ?", "jinzhu2").Where("age = ?", 20).Find(&users)
  9. // `Where("name = ?", "jinzhu2")` 也是调用的第一个方法,也会创建一个新 `Statement`
  10. // `Where("age = ?", 20)` 会复用 `Statement`,并将条件添加至这个 `Statement`
  11. // `Find(&users)` 是一个 finisher 方法,它运行注册的查询回调,生成并运行下面这条 SQL:
  12. // SELECT * FROM users WHERE name = 'jinzhu2' AND age = 20;
  13. db.Find(&users)
  14. // 对于这个 `新建会话模式` 的 `*gorm.DB` 实例来说,`Find(&users)` 是一个 finisher 方法也是第一个调用的方法。
  15. // 它创建了一个新的 `Statement` 运行注册的查询回调,生成并运行下面这条 SQL:
  16. // SELECT * FROM users;

示例 2:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // db 是一个刚完成初始化的 *gorm.DB 实例,这是一个 `新建会话`
  3. tx := db.Where("name = ?", "jinzhu")
  4. // `Where("name = ?", "jinzhu")` 是第一个被调用的方法,它创建了一个新的 `Statement` 并添加条件
  5. tx.Where("age = ?", 18).Find(&users)
  6. // `tx.Where("age = ?", 18)` 会复用上面的那个 `Statement`,并向其添加条件
  7. // `Find(&users)` 是一个 finisher 方法,它运行注册的查询回调,生成并运行下面这条 SQL:
  8. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18
  9. tx.Where("age = ?", 28).Find(&users)
  10. // `tx.Where("age = ?", 18)` 同样会复用上面的那个 `Statement`,并向其添加条件
  11. // `Find(&users)` 是一个 finisher 方法,它运行注册的查询回调,生成并运行下面这条 SQL:
  12. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18 AND age = 20;

注意 在示例 2 中,第一个查询会影响第二个查询生成的 SQL ,因为 GORM 复用 Statement 这可能会引发预期之外的问题,请参考 线程安全 了解如何避免该问题。

线程安全

新初始化的 *gorm.DB 或调用 新建会话方法 后,GORM 会创建新的 Statement 实例。因此想要复用 *gorm.DB,您需要确保它们处于 新建会话模式,例如:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // 安全的使用新初始化的 *gorm.DB
  3. for i := 0; i < 100; i++ {
  4. go db.Where(...).First(&user)
  5. }
  6. tx := db.Where("name = ?", "jinzhu")
  7. // 不安全的复用 Statement
  8. for i := 0; i < 100; i++ {
  9. go tx.Where(...).First(&user)
  10. }
  11. ctx, _ := context.WithTimeout(context.Background(), time.Second)
  12. ctxDB := db.WithContext(ctx)
  13. // 在 `新建会话方法` 之后是安全的
  14. for i := 0; i < 100; i++ {
  15. go ctxDB.Where(...).First(&user)
  16. }
  17. ctx, _ := context.WithTimeout(context.Background(), time.Second)
  18. ctxDB := db.Where("name = ?", "jinzhu").WithContext(ctx)
  19. // 在 `新建会话方法` 之后是安全的
  20. for i := 0; i < 100; i++ {
  21. go ctxDB.Where(...).First(&user) // `name = 'jinzhu'` 会应用到每次循环中
  22. }
  23. tx := db.Where("name = ?", "jinzhu").Session(&gorm.Session{WithConditions: true})
  24. // 在 `新建会话方法` 之后是安全的
  25. for i := 0; i < 100; i++ {
  26. go tx.Where(...).First(&user) // `name = 'jinzhu'` 会应用到每次循环中
  27. }