实习小记 - GORM的关联处理

最近写后端的业务逻辑,经常踩GORM的各种坑。这篇文章目的是梳理下GORM中的关联处理(association),以及自己踩过的一些坑。

我遇到的坑们

关于Gorm多态关联

有个需求需要每次更改操作都需要对应到历史记录。为了实现这个功能,需要一张额外的records表,且这张表可以同时记录广告位和广告位分组的变更信息。如果使用简单的外键的话,一个外键不可以同时对应两张表。所以在这里,需要使用多态关联而不是外键关联。Gorm提供了对于多态关联的支持。假设我们有Cat, Dog, Toy三张表,CatDog都可以拥有Toy. 使用gorm实现此关系的代码如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Cat struct {
ID int
Name string
Toy []Toy `gorm:"polymorphic:Owner;"`
}

type Dog struct {
ID int
Name string
Toy []Toy `gorm:"polymorphic:Owner;"`
}

type Toy struct {
ID int
Name string
OwnerID int
OwnerType string
}

其中被拥有的数据对象(这里的Toy)将被称为Owner。这个结构体里面需要手动添加OwnerIdOwnerTypeOwnerID这个属性对应的是从属对象的主键,而OwnerType则记录了被拥有对象的type。比如如果是DogToy, ToyOwnerType值就会为Dog。我们在所有者对象CatDog中,只需要定义一个Toy的数组,并且声明polymorphic关键字为Owner就可以了。

当我们想进行查询时,比如想获得所有Cat对应的玩具,只需要使用

1
2
var toys []*Toy
db.Model(&cat).Related(&toys, "Toy")

需要注意的就是OwnerID这个属性会自动关联到对应表的primary_key,所以使用多态千万不要忘了定义primary_key。

Antomigrate的一个小坑

Gorm在automigrate时是不支持migrate primary_key的。也就是说如果一开始忘记定义主键了,后来你想通过修改表定义重新automigrate一次来修改表结构是不可行的。只能手动去数据库里加上主键。

Gorm Associations用法总结

Belongs to

belongs to是一种one-to-one connection。

例子,一个用户profile属于一个用户。默认的外键是结构体的名字+主键的名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type User struct {
gorm.Model
Name string
}

type Profile struct {
gorm.Model
UserID int // default foreign key
User User
Name string
}

// 也可以手动指定foreign key
type Profile struct {
gorm.Model
UserRefer uint
User User `gorm:"foreignkey:UserRefer"`
Name string
}

Association ForeignKey

默认情况下,gorm会用owner的主键作为外键的值。但是也可以通过association_foreignkey来指定外键的值

1
2
3
4
5
6
7
8
9
10
11
12
type User struct {
gorm.Model
Refer string
Name string
}

type Profile struct {
gorm.Model
Name string
User User `gorm:"association_foreignkey:Refer"` // use Refer as association foreign key
UserRefer string
}

Usage

1
db.Model(&user).Related(&profile) // user一般是外面传进来的参数,profile是用来接收的对象