查询
检索单个对象
GORM 提供了 First、Take、Last 方法从数据库中检索单个对象,查询数据库时会添加 LIMIT 1 条件,如果没有找到记录则会返回 ErrRecordNotFound 错误。
泛型 API
ctx := context.Background()
user, err := gorm.G[User](db).First(ctx)
user, err := gorm.G[User](db).Take(ctx)
user, err := gorm.G[User](db).Last(ctx)
errors.Is(err, gorm.ErrRecordNotFound)传统 API
db.First(&user)
db.Take(&user)
db.Last(&user)
result := db.First(&user)
result.RowsAffected
result.Error
errors.Is(result.Error, gorm.ErrRecordNotFound)如果你想避免
ErrRecordNotFound错误,可以使用Find,例如db.Limit(1).Find(&user),Find方法同时接受结构体和切片数据
对单个对象不加限制地使用
Find,例如db.Find(&user),会查询整个表并只返回第一个对象,这既不确定性能也不好
First 和 Last 方法会分别查找按主键排序的第一条和最后一条记录。它们只有在向方法传入目标结构体的指针作为参数,或使用 db.Model() 指定了模型时才有效。此外,如果相关模型没有定义主键,模型将按第一个字段排序。例如:
var user User
var users []User
db.First(&user)
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
result := map[string]interface{}{}
db.Table("users").First(&result)
result := map[string]interface{}{}
db.Table("users").Take(&result)
type Result struct {
ID int
Name string
}
var joinResult Result
db.Table("user_languages ul").
Select("l.id, l.name").
Joins("JOIN languages l ON l.code = ul.language_code").
Order("l.name DESC").
First(&joinResult)
db.Table("user_languages ul").
Select("l.id, l.name").
Joins("JOIN languages l ON l.code = ul.language_code").
Order("l.name DESC").
Take(&joinResult)
type Language struct {
Code string
Name string
}
db.First(&Language{})
通过主键检索对象
如果主键是数字,可以使用内联条件通过主键检索对象。当使用字符串时,需要格外小心以避免 SQL 注入;详情请查看安全章节。
泛型 API
ctx := context.Background()
user, err := gorm.G[User](db).Where("id = ?", 10).First(ctx)
user, err := gorm.G[User](db).Where("id = ?", "10").First(ctx)
users, err := gorm.G[User](db).Where("id IN ?", []int{1,2,3}).Find(ctx)
user, err := gorm.G[User](db).Where("id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a").First(ctx)
传统 API
db.First(&user, 10)
db.First(&user, "10")
db.Find(&users, []int{1,2,3})
如果主键是字符串(例如 uuid),查询将如下写法:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
当目标对象已有主键值时,将使用主键构建条件,例如:
var user = User{ID: 10}
db.First(&user)
var result User
db.Model(User{ID: 10}).First(&result)
注意: 如果你使用了 GORM 的特殊字段类型,如
gorm.DeletedAt,它会执行不同的查询来检索对象。
type User struct {
ID string `gorm:"primarykey;size:16"`
Name string `gorm:"size:24"`
DeletedAt gorm.DeletedAt `gorm:"index"`
}
var user = User{ID: 15}
db.First(&user)
检索所有对象
result := db.Find(&users)
result.RowsAffected
result.Error 条件
字符串条件
db.Where("name = ?", "jinzhu").First(&user)
db.Where("name <> ?", "jinzhu").Find(&users)
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
db.Where("name LIKE ?", "%jin%").Find(&users)
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
db.Where("updated_at > ?", lastWeek).Find(&users)
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
如果对象的主键已被设置,那么条件查询不会覆盖主键的值,而是将其作为一个
and条件使用。例如:var user = User{ID: 10} db.Where("id = ?", 20).First(&user)
这个查询会返回 `record not found` 错误。所以在你想使用 `user` 变量从数据库获取新值之前,需要将主键属性(如 `id`)设置为 nil。
结构体和 Map 条件
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
db.Where([]int64{20, 21, 22}).Find(&users)
注意 使用结构体查询时,GORM 只会用非零字段进行查询,也就是说如果你的字段值为
0、''、false或其他零值,它不会被用来构建查询条件,例如:
db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
要在查询条件中包含零值,可以使用 Map,它会将所有键值对作为查询条件,例如:
db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
更多信息请查看指定结构体搜索字段。
指定结构体搜索字段
使用结构体搜索时,你可以通过向 Where() 传入相关的字段名或数据库名来指定要用于查询条件的特定值,例如:
db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)
内联条件
查询条件可以像 Where 一样内联到 First 和 Find 等方法中。
db.First(&user, "id = ?", "string_primary_key")
db.Find(&user, "name = ?", "jinzhu")
db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
db.Find(&users, User{Age: 20})
db.Find(&users, map[string]interface{}{"age": 20})
NOT 条件
构建 NOT 条件,用法与 Where 类似
db.Not("name = ?", "jinzhu").First(&user)
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
db.Not([]int64{1,2,3}).First(&user)
OR 条件
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
更复杂的 SQL 查询,请参考高级查询中的分组条件。
选择特定字段
Select 允许你指定要从数据库中检索的字段。否则,GORM 默认会选择所有字段。
db.Select("name", "age").Find(&users)
db.Select([]string{"name", "age"}).Find(&users)
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
另请查看智能选择字段
排序
指定从数据库检索记录时的排序方式
db.Order("age desc, name").Find(&users)
db.Order("age desc").Order("name").Find(&users)
db.Clauses(clause.OrderBy{
Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
Limit & Offset
Limit 指定要检索的最大记录数Offset 指定在开始返回记录之前要跳过的记录数
db.Limit(3).Find(&users)
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
db.Offset(3).Find(&users)
db.Limit(10).Offset(5).Find(&users)
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
关于如何制作分页器的详情请参考分页
Group By & Having
type result struct {
Date time.Time
Total int
}
db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
defer rows.Close()
for rows.Next() {
...
}
rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
defer rows.Close()
for rows.Next() {
...
}
type Result struct {
Date time.Time
Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)Distinct
从模型中选择不同的值
db.Distinct("name", "age").Order("name, age desc").Find(&results)Distinct 也可以与 Pluck 和 Count 一起使用
Joins
泛型 API
新的 GORM 泛型接口为关联查询(Joins)带来了增强的支持,提供了更灵活的关联方法、更强大的查询能力,以及大幅简化的复杂查询构建方式。
- Joins:轻松指定不同的连接类型(如
InnerJoin、LeftJoin),并基于关联自定义连接条件,使复杂的跨表查询更加清晰直观。
users, err := gorm.G[User](db).Joins(clause.Has("Company"), nil).Find(ctx)
user, err = gorm.G[User](db).Joins(clause.LeftJoin.Association("Company"), func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {
db.Where(map[string]any{"name": company.Name})
return nil
}).Where(map[string]any{"name": user.Name}).First(ctx)
users, err = gorm.G[User](db).Joins(clause.LeftJoin.AssociationFrom("Company", gorm.G[Company](DB).Select("Name")).As("t"),
func(db gorm.JoinBuilder, joinTable clause.Table, curTable clause.Table) error {
db.Where("?.name = ?", joinTable, u.Company.Name)
return nil
},
).Find(ctx)传统 API
指定 Joins 条件
type result struct {
Name string
Email string
}
db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
...
}
db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)Joins 预加载
你可以使用 Joins 通过单条 SQL 进行关联的即时加载,例如:
db.Joins("Company").Find(&users)
db.InnerJoins("Company").Find(&users)
带条件的连接
db.Joins("Company", db.Where(&Company{Alive: true})).Find(&users)
更多信息请参考预加载(即时加载)。
连接派生表
你还可以使用 Joins 连接一个派生表。
type User struct {
Id int
Age int
}
type Order struct {
UserId int
FinishedAt *time.Time
}
query := db.Table("order").Select("MAX(order.finished_at) as latest").Joins("left join user user on order.user_id = user.id").Where("user.age > ?", 18).Group("order.user_id")
db.Model(&Order{}).Joins("join (?) q on order.finished_at = q.latest", query).Scan(&results)
Scan
将结果扫描到结构体中的方式与我们使用 Find 的方式类似
type Result struct {
Name string
Age int
}
var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)评论
登录后参与评论
KnowForge