0Pricing
Go Academy · 课时

关联

建立关系模型

关联 是 CoddyKit 上的免费 Go Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

关系建模

GORM 的关联会将表之间的关系映射到结构体字段上。四种关联类型是一对一、一对多、属于和多对多。

  • 将外键表示为结构体字段
  • 自动加载相关数据
  • 同时创建相关记录

一对多

一个用户可以拥有多篇文章。父记录持有子记录切片,子记录持有指向父记录的外键。

type User struct {
    gorm.Model
    Name  string
    Posts []Post
}

type Post struct {
    gorm.Model
    Title  string
    UserID uint
}

属于

从子记录的角度看,一篇文章属于一个用户。添加一个 User 字段以及 UserID 外键即可表达这种关系。

type Post struct {
    gorm.Model
    Title  string
    UserID uint
    User   User
}

一对一

一对一关联类似于一对多关联,但只有一条相关记录,例如一个用户拥有一个个人资料。子记录持有外键。

type User struct {
    gorm.Model
    Profile Profile
}

type Profile struct {
    gorm.Model
    UserID uint
    Bio    string
}

多对多

用户和角色通过连接表形成多对多关系。many2many 标签用于命名连接表,写在 Roles 字段后的反引号中,形式为 gorm:"many2many:user_roles;"。

type User struct {
    gorm.Model
    // Roles field tag: gorm:"many2many:user_roles;"
    Roles []Role
}

type Role struct {
    gorm.Model
    Name string
}

Preload

默认情况下不会加载相关数据。Preload 会让 GORM 通过单独的查询获取关联数据,从而避免手动执行额外的查找。

var user User
db.Preload("Posts").First(&user, 1)
fmt.Println(len(user.Posts))

嵌套 Preload

您可以使用点号路径跨多个层级进行预加载,在一次调用链中加载用户的文章以及每篇文章的评论。

db.Preload("Posts.Comments").First(&user, 1)

可运行的关联模拟

GORM 需要数据库,因此这个可运行示例使用内存切片模拟一对多关系和预加载。

package main

import "fmt"

type Post struct {
    ID     int
    UserID int
    Title  string
}

type User struct {
    ID    int
    Name  string
    Posts []Post
}

func preload(u *User, all []Post) {
    for _, p := range all {
        if p.UserID == u.ID {
            u.Posts = append(u.Posts, p)
        }
    }
}

func main() {
    u := User{ID: 1, Name: "Ada"}
    all := []Post{{1, 1, "Hello"}, {2, 1, "World"}, {3, 2, "Other"}}
    preload(&u, all)
    fmt.Printf("%s has %d posts\n", u.Name, len(u.Posts))
}

使用关联创建

如果在调用 Create 前设置切片字段,GORM 就会同时插入父记录及其子记录,并自动连接外键。

user := User{Name: "Ada", Posts: []Post{{Title: "Hi"}}}
db.Create(&user)

N+1 问题

遍历父记录并在每次迭代中查询子记录,会导致每个父记录执行一次查询,这就是 N+1 问题。Preload 会将相关数据的获取合并到一次额外查询中,从而解决这个问题。

关联模式

Association API 可以追加、替换、删除或清空相关记录,而无需手动处理外键,例如 db.Model(&user).Association("Posts").Append(&post)。

快速检查

请测试您对关联的了解。

总结

您已经学会了关联:

  • 使用一对一、一对多、属于和多对多表示模型关系
  • 外键是 UserID 这样的结构体字段
  • Preload 会加载关联并避免 N+1 问题
  • 创建带有子记录的父记录时会连接外键
  • 关联模式可以追加、替换或清空关系

常见问题解答

「关联」课时是免费的吗?

是的 — 「关联」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「关联」这节课中我会学到什么?

建立关系模型 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「关联」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 模型与 AutoMigrate
  2. CRUD 操作
  3. 关联
  4. 钩子与作用域
← 返回 Go Academy