前言
最近在做项目,发现关联查询的时候,用Preload查不出来关联的对象,最中通过在网上查资料解决了,在这里记录一下。
关联关系
个人认为,关联关系主要有主对从,从对主两种。这里跟一般理解的一对一、一对多、多对多关联关系不一样。我是以主从表的关系来看待关联关系的。看起来有点绕,下面有具体的🌰。
下面举个简单实例看一下:
- 学生
-
- ID
-
- 姓名
-
- 学校ID
- 学校
-
- ID
-
- 名称
学生对象上有个属性叫学校,这个时候学校就是主表,学生就是从表,学生的学校字段就是外键。
关系一主对从(Has One)
Have One表示有一个,如果A Have One B,显然关系是A>B,即A是主,B是从。
对于上面学生和学校的关系,下面定义两个结构体
type Person struct {
gorm.Model
Name string
SchoolID uint // 这个字段是关键,必须要有。在第二个sql语句中会用这个字段的值去匹配school表的ID字段
}
type School struct {
gorm.Model
Name string
Person *Person
}
即主表中有一个属性是从表,这里School就是A, Person就是B,我们主要查主表,顺便带出从表。这个时候查询的代码该怎么写呢?
db.WithContext(ctx).Debug().Preload("Person").Find(&schools)
此时对于执行的sql语句为:
// 1
SELECT * FROM `schools` WHERE `schools`.`deleted_at` IS NULL
// 2
SELECT * FROM `persons` WHERE `persons`.`school_id` = 1 AND `persons`.`deleted_at` IS NULL
这个时候查出来的结果中School.Person属性上就有值,而且如果是数组,Scheool.Person定义的也是数组,则也会有值,例如有2个学生是同一个学校的,则结果为:
关系二从对主(Belongs To)
Belongs To表示属于,如果A Belongs To B,显然关系是A<B,即A是从,B是主。
对于上面学生和学校的关系,下面定义两个结构体
type Person struct {
gorm.Model
Name string
SchoolID uint // 这个字段是关键,必须要有。在第二个sql语句中会根据这个字段的值去查school表
School School // 这里不能定义成数组,以为一条person记录只要一个school_Id
}
type School struct {
gorm.Model
Name string
}
即从表中有一个属性是主表,这里Person就是A, School就是B,我们主要查从表,顺便带出主表。这个时候查询的代码该怎么写呢?
db.WithContext(ctx).Debug().Preload("School").Find(&persons)
此时对于执行的sql语句为:
// 1
SELECT * FROM `persons` WHERE `persons`.`deleted_at` IS NULL
// 2
SELECT * FROM `schools` WHERE `schools`.`id` IN (1,2) AND `schools`.`deleted_at` IS NULL
结果为:
persons[1].school字段为空,所以School字段为空 persons[2].school字段为1并且schoolID=1的记录存在,所以School字段不为空 persons[3].school字段为2b并且schoolID=2的记录不存在,所以School字段为空
总结
总结上面我们知道,无论主从对象,外键字段必须在从表上存在,并且在对象上有定义出来,即Person.SchoolID字段。然后,如果是查主表附带查从表,则需要在主表(School)上定义从表属性,可以是数组,即School.Person;如果要根据查从表附带查主表,则需要在从表(Person)上定义主表的属性,不能是数组,即Person.School。
参考
[1]Has One
[2]Belongs To