高级查询

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. 智能选择字段

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

3. 子查询

db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
1
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

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

比如有一个用户属性表,查询用户的时候需要将其的性别和年龄查询出来:

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
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

保存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

会产生两条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

只要给UserProfile添加上相应的tag即可。

关联标签

标签描述
foreignKey指定当前模型的列作为连接表的外键
references指定引用表的列名,其将被映射为连接表外键
polymorphic指定多态类型,比如模型名
polymorphicValue指定多态值、默认表名
many2many指定连接表表名
joinForeignKey指定连接表的外键列名,其将被映射到当前表
joinReferences指定连接表的外键列名,其将被映射到引用表
constraint关系约束,例如:OnUpdateOnDelete

4.1 查询

	var users []User
	err := DB.Preload("UserProfile").Find(&users).Error
	fmt.Println(err)
	fmt.Println(users)
1
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

从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