高级查询
1. scope
作用域允许你复用通用的逻辑,这种共享逻辑需要定义为类型func(*gorm.DB) *gorm.DB
。
例子:
func Paginate(r *http.Request) func(db *gorm.DB) *gorm.DB {
return func (db *gorm.DB) *gorm.DB {
q := r.URL.Query()
page, _ := strconv.Atoi(q.Get("page"))
if page == 0 {
page = 1
}
pageSize, _ := strconv.Atoi(q.Get("page_size"))
switch {
case pageSize > 100:
pageSize = 100
case pageSize <= 0:
pageSize = 10
}
offset := (page - 1) * pageSize
return db.Offset(offset).Limit(pageSize)
}
}
db.Scopes(Paginate(r)).Find(&users)
db.Scopes(Paginate(r)).Find(&articles)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2. 智能选择字段
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3. 子查询
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
1
2
2
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{})
1
2
3
4
5
6
2
3
4
5
6
4. 关联操作
CREATE TABLE `gorm`.`user_profiles` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`sex` tinyint(4) NULL DEFAULT NULL,
`age` int(10) NULL DEFAULT NULL,
`user_id` int(20) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4;
1
2
3
4
5
6
7
2
3
4
5
6
7
比如有一个用户属性表,查询用户的时候需要将其的性别和年龄查询出来:
type UserProfile struct {
ID int64
UserId int64
Sex int
Age int
}
func (u UserProfile) TableName() string {
return "user_profiles"
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
type User struct {
ID int64
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
CreateTime int64 `gorm:"column:createtime"`
UserProfile UserProfile
}
1
2
3
4
5
6
7
2
3
4
5
6
7
保存User
var user = User{
Username: "ms",
Password: "ms",
CreateTime: time.Now().UnixMilli(),
UserProfile: UserProfile{
Sex: 0,
Age: 20,
},
}
DB.Save(&user)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
会产生两条sql,users表和user_profiles表都有数据
这是因为默认的外键是结构体名字+下划线+id,即UserId或者表字段是user_id
如果将user_profiles表中的user_id改为other_id就会失败。
type User struct {
ID int64
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
CreateTime int64 `gorm:"column:createtime"`
UserProfile UserProfile `gorm:"foreignKey:OtherId"`
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
只要给UserProfile添加上相应的tag即可。
关联标签
标签 | 描述 |
---|---|
foreignKey | 指定当前模型的列作为连接表的外键 |
references | 指定引用表的列名,其将被映射为连接表外键 |
polymorphic | 指定多态类型,比如模型名 |
polymorphicValue | 指定多态值、默认表名 |
many2many | 指定连接表表名 |
joinForeignKey | 指定连接表的外键列名,其将被映射到当前表 |
joinReferences | 指定连接表的外键列名,其将被映射到引用表 |
constraint | 关系约束,例如:OnUpdate 、OnDelete |
4.1 查询
var users []User
err := DB.Preload("UserProfile").Find(&users).Error
fmt.Println(err)
fmt.Println(users)
1
2
3
4
2
3
4
Preload预加载,直接加载关联关系
也可以使用joins进行加载关联数据:
var users []User
err := DB.Joins("UserProfile").Find(&users).Error
fmt.Println(err)
fmt.Println(users)
1
2
3
4
2
3
4
从sql中能看的出来,使用了left join。
如果不想要User的数据,只想要关联表的数据,可以这么做:
var user User
DB.Where("id=?", 25).Take(&user)
var userProfile UserProfile
err := DB.Model(&user).Association("UserProfile").Find(&userProfile)
fmt.Println(err)
fmt.Println(userProfile)
1
2
3
4
5
6
2
3
4
5
6