智能选择字段
GORM 允许通过 Select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段,例如:
type User struct { ID uint Name string Age int Gender string // 假设后面还有几百个字段... } type APIUser struct { ID uint Name string } // 查询时会自动选择 `id`, `name` 字段 db.Model(&User{}).Limit(10).Find(&APIUser{}) // SELECT `id`, `name` FROM `users` LIMIT 10 |
注意
QueryFields
模式会根据当前 model 的所有字段名称进行 select。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ QueryFields: true, }) db.Find(&user) // SELECT `users`.`name`, `users`.`age`, ... FROM `users` // 带上这个选项 // Session Mode db.Session(&gorm.Session{QueryFields: true}).Find(&user) // SELECT `users`.`name`, `users`.`age`, ... FROM `users` |
Locking (FOR UPDATE)
GORM 支持多种类型的锁,例如:
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users) // SELECT * FROM `users` FOR UPDATE db.Clauses(clause.Locking{ Strength: "SHARE", Table: clause.Table{Name: clause.CurrentTable}, }).Find(&users) // SELECT * FROM `users` FOR SHARE OF `users` db.Clauses(clause.Locking{ Strength: "UPDATE", Options: "NOWAIT", }).Find(&users) // SELECT * FROM `users` FOR UPDATE NOWAIT |
查看 原生 SQL 及构造器 获取详情
子查询
子查询可以嵌套在查询中,GORM 允许在使用 *gorm.DB
对象作为参数时生成子查询
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders) // SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders"); subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users") db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results) // SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%") |
From 子查询
GORM 允许您在 Table
方法中通过 FROM 子句使用子查询,例如:
db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18).Find(&User{}) // SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18 subQuery1 := db.Model(&User{}).Select("name") subQuery2 := db.Model(&Pet{}).Select("name") db.Table("(?) as u, (?) as p", subQuery1, subQuery2).Find(&User{}) // SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p |
Group 条件
使用 Group 条件可以更轻松的编写复杂 SQL
db.Where( db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")), ).Or( db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"), ).Find(&Pizza{}).Statement // SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge") |
带多个列的 In
带多个列的 In 查询
db.Where("(name, age, role) IN ?", [][]interface{}{{"jinzhu", 18, "admin"}, {"jinzhu2", 19, "user"}}).Find(&users) // SELECT * FROM users WHERE (name, age, role) IN (("jinzhu", 18, "admin"), ("jinzhu 2", 19, "user")); |
命名参数
GORM 支持 sql.NamedArg 和 map[string]interface{}{}
形式的命名参数,例如:
db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user) // SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu"}).First(&user) // SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" ORDER BY `users`.`id` LIMIT 1 |
查看 原生 SQL 及构造器 获取详情
Find 至 map
GORM 允许扫描结果至 map[string]interface{}
或 []map[string]interface{}
,此时别忘了指定 Model
或 Table
,例如:
result := map[string]interface{}{} db.Model(&User{}).First(&result, "id = ?", 1) var results []map[string]interface{} db.Table("users").Find(&results) |
FirstOrInit
获取第一条匹配的记录,或者根据给定的条件初始化一个实例(仅支持 sturct 和 map 条件)
// 未找到 user,则根据给定的条件初始化一条记录 db.FirstOrInit(&user, User{Name: "non_existing"}) // user -> User{Name: "non_existing"} // 找到了 `name` = `jinzhu` 的 user db.Where(User{Name: "jinzhu"}).FirstOrInit(&user) // user -> User{ID: 111, Name: "Jinzhu", Age: 18} // 找到了 `name` = `jinzhu` 的 user db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"}) // user -> User{ID: 111, Name: "Jinzhu", Age: 18} |
如果没有找到记录,可以使用包含更多的属性的结构体初始化 user,Attrs
不会被用于生成查询 SQL
// 未找到 user,则根据给定的条件以及 Attrs 初始化 user db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user) // SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1; // user -> User{Name: "non_existing", Age: 20} // 未找到 user,则根据给定的条件以及 Attrs 初始化 user db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user) // SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1; // user -> User{Name: "non_existing", Age: 20} // 找到了 `name` = `jinzhu` 的 user,则忽略 Attrs db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 20}).FirstOrInit(&user) // SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1; // user -> User{ID: 111, Name: "Jinzhu", Age: 18} |
不管是否找到记录,Assign
都会将属性赋值给 struct,但这些属性不会被用于生成查询 SQL,也不会被保存到数据库
// 未找到 user,根据条件和 Assign 属性初始化 struct db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user) // user -> User{Name: "non_existing", Age: 20} // 找到 `name` = `jinzhu` 的记录,依然会更新 Assign 相关的属性 db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 20}).FirstOrInit(&user) // SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1; // user -> User{ID: 111, Name: "Jinzhu", Age: 20} |
FirstOrCreate
获取匹配的第一条记录或者根据给定条件创建一条新纪录(仅 struct, map 条件有效),RowsAffected
返回创建、更新的记录数
// 未找到 User,根据给定条件创建一条新纪录 result := db.FirstOrCreate(&user, User{Name: "non_existing"}) // INSERT INTO "users" (name) VALUES ("non_existing"); // user -> User{ID: 112, Name: "non_existing"} // result.RowsAffected // => 1 // 找到 `name` = `jinzhu` 的 User result := db.Where(User{Name: "jinzhu"}).FirstOrCreate(&user) // user -> User{ID: 111, Name: "jinzhu", "Age": 18} // result.RowsAffected // => 0 |
如果没有找到记录,可以使用包含更多的属性的结构体创建记录,Attrs
不会被用于生成查询 SQL 。
// 未找到 user,根据条件和 Assign 属性创建记录 db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1; // INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{ID: 112, Name: "non_existing", Age: 20} // 找到了 `name` = `jinzhu` 的 user,则忽略 Attrs db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1; // user -> User{ID: 111, Name: "jinzhu", Age: 18} |
不管是否找到记录,Assign
都会将属性赋值给 struct,并将结果写回数据库
// 未找到 user,根据条件和 Assign 属性创建记录 db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1; // INSERT INTO "users" (name, age) VALUES ("non_existing", 20); // user -> User{ID: 112, Name: "non_existing", Age: 20} // 找到了 `name` = `jinzhu` 的 user,依然会根据 Assign 更新记录 db.Where(User{Name: "jinzhu"}).Assign(User{Age: 20}).FirstOrCreate(&user) // SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1; // UPDATE users SET age=20 WHERE id = 111; // user -> User{ID: 111, Name: "jinzhu", Age: 20} |
优化器、索引提示
优化器提示用于控制查询优化器选择某个查询执行计划,GORM 通过 gorm.io/hints
提供支持,例如:
import "gorm.io/hints" db.Clauses(hints.New("MAX_EXECUTION_TIME(10000)")).Find(&User{}) // SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users` |
索引提示允许传递索引提示到数据库,以防查询计划器出现混乱。
import "gorm.io/hints" db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{}) // SELECT * FROM `users` USE INDEX (`idx_user_name`) db.Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find(&User{}) // SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)" |
参考 优化器提示、索引、备注 获取详情
迭代
GORM 支持通过行进行迭代
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Rows() defer rows.Close() for rows.Next() { var user User // ScanRows 方法用于将一行记录扫描至结构体 db.ScanRows(rows, &user) // 业务逻辑... } |
FindInBatches
用于批量查询并处理记录
// 每次批量处理 100 条 result := db.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { for _, result := range results { // 批量处理找到的记录 } tx.Save(&results) tx.RowsAffected // 本次批量操作影响的记录数 batch // Batch 1, 2, 3 // 如果返回错误会终止后续批量操作 return nil }) result.Error // returned error result.RowsAffected // 整个批量操作影响的记录数 |
查询钩子
对于查询操作,GORM 支持 AfterFind
钩子,查询记录后会调用它,详情请参考 钩子
func (u *User) AfterFind(tx *gorm.DB) (err error) { if u.Role == "" { u.Role = "user" } return } |
Pluck
Pluck 用于从数据库查询单个列,并将结果扫描到切片。如果您想要查询多列,您应该使用 Select
和 Scan
var ages []int64 db.Model(&users).Pluck("age", &ages) var names []string db.Model(&User{}).Pluck("name", &names) db.Table("deleted_users").Pluck("name", &names) // Distinct Pluck db.Model(&User{}).Distinct().Pluck("Name", &names) // SELECT DISTINCT `name` FROM `users` // 超过一列的查询,应该使用 `Scan` 或者 `Find`,例如: db.Select("name", "age").Scan(&users) db.Select("name", "age").Find(&users) |
Scope
Scopes
允许你指定常用的查询,可以在调用方法时引用这些查询
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { return db.Where("amount > ?", 1000) } func PaidWithCreditCard(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func PaidWithCod(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { return func (db *gorm.DB) *gorm.DB { return db.Where("status IN (?)", status) } } db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders) // 查找所有金额大于 1000 的信用卡订单 db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders) // 查找所有金额大于 1000 的货到付款订单 db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders) // 查找所有金额大于 1000 且已付款或已发货的订单 |
查看 Scopes 获取详情
Count
Count 用于获取匹配的记录数
var count int64 db.Model(&User{}).Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Count(&count) // SELECT count(1) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2' db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count) // SELECT count(1) FROM users WHERE name = 'jinzhu'; (count) db.Table("deleted_users").Count(&count) // SELECT count(1) FROM deleted_users; // Count with Distinct db.Model(&User{}).Distinct("name").Count(&count) // SELECT COUNT(DISTINCT(`name`)) FROM `users` db.Table("deleted_users").Select("count(distinct(name))").Count(&count) // SELECT count(distinct(name)) FROM deleted_users // Count with Group users := []User{ {Name: "name1"}, {Name: "name2"}, {Name: "name3"}, {Name: "name3"}, } db.Model(&User{}).Group("name").Count(&count) count // => 3 |