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

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

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

After a Chain method, Finisher Method, GORM returns an initialized *gorm.DB instance, which is NOT safe to reuse anymore, or new generated SQL might be polluted by the previous conditions, for example:

  1. queryDB := DB.Where("name = ?", "jinzhu")
  2. queryDB.First(&user)
  3. // SELECT * FROM users WHERE name = "jinzhu"
  4. queryDB.Where("name = ?", "jinzhu2").First(&user2)
  5. // SELECT * FROM users WHERE name = "jinzhu" AND name = "jinzhu2"

In order to reuse a initialized *gorm.DB instance, you can use a New Session Method to create a shareable *gorm.DB, e.g:

  1. queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{})
  2. queryDB.First(&user)
  3. // SELECT * FROM users WHERE name = "jinzhu"
  4. queryDB.Where("name = ?", "jinzhu2").First(&user2)
  5. // SELECT * FROM users WHERE name = "jinzhu2"

链式方法

Chain methods are methods to modify or add Clauses to current Statement, like:

Where, Select, Omit, Joins, Scopes, Preload, Raw (Raw can’t be used with other chainable methods to build SQL)…

Here is the full lists, also check out the SQL Builder for more details about Clauses.

Finisher 方法

Finishers are immediate methods that execute registered callbacks, which will generate and execute SQL, like those methods:

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

Check out the full lists here.

New Session Method

GORM defined Session, WithContext, Debug methods as New Session Method, refer Session for more details.

After a Chain method, Finisher Method, GORM returns an initialized *gorm.DB instance, which is NOT safe to reuse anymore, you should use a New Session Method to mark the *gorm.DB as shareable.

Let’s explain it with examples:

Example 1:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // db is a new initialized `*gorm.DB`, which is safe to reuse
  3. db.Where("name = ?", "jinzhu").Where("age = ?", 18).Find(&users)
  4. // `Where("name = ?", "jinzhu")` is the first chain method call, it will create an initialized `*gorm.DB` instance, aka `*gorm.Statement`
  5. // `Where("age = ?", 18)` is the second chain method call, it reuses the above `*gorm.Statement`, adds new condition `age = 18` to it
  6. // `Find(&users)` is a finisher method, it executes registered Query Callbacks, which generates and runs the following 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")` is also the first chain method call, it creates a new `*gorm.Statement`
  10. // `Where("age = ?", 20)` reuses the above `Statement`, and add conditions to it
  11. // `Find(&users)` is a finisher method, it executes registered Query Callbacks, generates and runs the following SQL:
  12. // SELECT * FROM users WHERE name = 'jinzhu2' AND age = 20;
  13. db.Find(&users)
  14. // `Find(&users)` is a finisher method call, it also creates a new `Statement` and executes registered Query Callbacks, generates and runs the following SQL:
  15. // SELECT * FROM users;

(Bad) Example 2:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // db is a new initialized *gorm.DB, which is safe to reuse
  3. tx := db.Where("name = ?", "jinzhu")
  4. // `Where("name = ?", "jinzhu")` returns an initialized `*gorm.Statement` instance after chain method `Where`, which is NOT safe to reuse
  5. // good case
  6. tx.Where("age = ?", 18).Find(&users)
  7. // `tx.Where("age = ?", 18)` use the above `*gorm.Statement`, adds new condition to it
  8. // `Find(&users)` is a finisher method call, it executes registered Query Callbacks, generates and runs the following SQL:
  9. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18
  10. // bad case
  11. tx.Where("age = ?", 28).Find(&users)
  12. // `tx.Where("age = ?", 18)` also use the above `*gorm.Statement`, and keep adding conditions to it
  13. // So the following generated SQL is polluted by the previous conditions:
  14. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18 AND age = 28;

Example 3:

  1. db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
  2. // db is a new initialized *gorm.DB, which is safe to reuse
  3. tx := db.Where("name = ?", "jinzhu").Session(&gorm.Session{})
  4. tx := db.Where("name = ?", "jinzhu").WithContext(context.Background())
  5. tx := db.Where("name = ?", "jinzhu").Debug()
  6. // `Session`, `WithContext`, `Debug` returns `*gorm.DB` marked as safe to reuse, newly initialized `*gorm.Statement` based on it keeps current conditions
  7. // good case
  8. tx.Where("age = ?", 18).Find(&users)
  9. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 18
  10. // good case
  11. tx.Where("age = ?", 28).Find(&users)
  12. // SELECT * FROM users WHERE name = 'jinzhu' AND age = 28;