GORM关联

826 阅读2分钟

前言

最近在做项目,发现关联查询的时候,用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个学生是同一个学校的,则结果为:

image.png

关系二从对主(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

结果为:

image.png

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