Gorm 关联关系彻底区分

https://images.unsplash.com/photo-1505686994434-e3cc5abf1330?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb

多对一 Belongs To 与 一对多 Has Many

// `User` 属于 `Company`,`CompanyID` 是外键
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company
}

type Company struct {
  ID   int
  Name string
}

一对多和多对一的区别在于语义侧重点不同。你可以说一个工人属于一个工厂也可以说一个工厂有多馆工人,前者是多对一,后者是一对多。

// Company 有多个 User,CompanyID 是外键
type User struct {
  gorm.Model
  Name      string
  CompanyID int
}

type Company struct {
  ID   int
  Name string
  Users User[]
}

在实际的操作中就是要看你是要查公司然后顺便知道这个公司下有多少工人,还是要知道一个工人对应的公司是什么。Gorm 提供了预加载 Preload 来做这一点 。所以多对一和一对多的区分前提是你知道自己要什么,然后定义它,最后用 Preload 来得到它。

说的罗嗦一点,假如你要想查公司然后顺便知道这个公司下有多少工人,你的脑子里浮现的是这样一个预加载语句,那么你就要把 Users 写在 Company 内部。

db.Preload("Users").Find(&company);

反之若你要想查一个工人对应的公司,应该是这样一个预加载语句,那么你就要把 Company 写在 User 内部。

db.Preload("Company").Find(&user);

外键仍然在User处,因为还是一个User对应一个Company。

一对一 Has One

官方文档的例子。一个用户有且只有一个信用卡(好奇怪的设定)。

// User 有一张 CreditCard,UserID 是外键
type User struct {
  gorm.Model
  CreditCard CreditCard
}

type CreditCard struct {
  gorm.Model
  Number string
  UserID uint
}

对应 Preload 如下。可以看到这个和多对一(Belongs To)很像。前者是后者的退化。所以还是要从语义上区分。严格的一对一语义需要使用 Has One,否则用 Belongs To。

多对多 Many To Many

多对多场景比较常见。下面是官方文档。需要指定一个中间表用于存放关系。

type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}