Day08:GORM快速入门08 many to many| 青训营

77 阅读3分钟

导读

本套笔记是学习完Gin框架之后的下一个阶段,通过Gin框架我们可以很方便的和前端进行交互,下一步就是和数据库进行交互,这就需要使用GORM。本套笔记就是带你快速上手GORM,学习完这个之后就可以完成大项目的开发了,后续还会进行大项目的讲解开发,制作不易,喜欢的就点个关注吧。

本套课程基于GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.进行讲解,是对这个的补充解释说明。

注意

代码详解大部分是注释的形式给出,请留意代码注释。

GORM介绍

GORM(Go Object Relational Mapping)是一个在 Go 语言中使用的优秀的 ORM(对象关系映射)库。它提供了简单且强大的 API,使开发人员能够轻松地与数据库进行交互

many to many

Many to Many 会在两个 model 中添加一张连接表,将不同表的id连接起来

例如:您的应用包含了 user 和 language,且一个 user 可以说多种 language,多个 user 也可以说一种 language。

// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

当使用 GORM 的 AutoMigrate 为 User 创建表时,GORM 会自动创建连接表

完整代码

package main  
  
import (  
"fmt"  
"gorm.io/driver/mysql"  
"gorm.io/gorm"  
"log"  
)  
  
type User struct {  
gorm.Model  
Languages []Language `gorm:"many2many:user_languages;"`  
}  
  
type Language struct {  
gorm.Model  
Name string  
}  
  
func main() {  
  
db, err := gorm.Open(mysql.New(mysql.Config{  
DSN: "root:Lycdemima1@@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local", // DSN data source name  
DefaultStringSize: 256, // string 类型字段的默认长度  
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持  
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引  
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列  
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置  
}), &gorm.Config{}) //连接数据库  
  
// 自动迁移数据库结构  
err = db.AutoMigrate(&User{}, &Language{})  
if err != nil {  
log.Fatal(err)  
}  
  
// 创建用户及关联的语言  
err = CreateUserWithLanguages(db, "john", "English")  
if err != nil {  
log.Fatal(err)  
}  
  
// 查询用户及关联的语言  
user, err := GetUserWithLanguages(db, 1)  
if err != nil {  
log.Fatal(err)  
}  
  
fmt.Println("User:", user)  
fmt.Println("Languages:")  
for _, lang := range user.Languages {  
fmt.Println(lang)  
}  
  
}  
  
func CreateUserWithLanguages(db *gorm.DB, username string, languageName string) error {  
language := Language{  
Name: languageName,  
}  
user := User{  
Languages: []Language{language},  
}  
result := db.Create(&user)  
return result.Error  
}  
  
func GetUserWithLanguages(db *gorm.DB, userID uint) (User, error) {  
var user User  
result := db.Preload("Languages").First(&user, userID)  
return user, result.Error  
}

表关系如图

image.png

image.png

image.png 连接池绑定他们的id

重写外健

对于 many2many 关系,连接表会同时拥有两个模型的外键,例如

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

type Language struct {
  gorm.Model
  Name string
}

若要重写它们,可以使用标签 foreignKeyreferencesjoinforeignKeyjoinReferences。当然,您不需要使用全部的标签,你可以仅使用其中的一个重写部分的外键、引用。

type User struct {
    gorm.Model
    Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;joinReferences:ProfileRefer"`
    Refer    uint      `gorm:"index:,unique"`
}

type Profile struct {
    gorm.Model
    Name      string
    UserRefer uint `gorm:"index:,unique"`
}

自引用

type User struct {
  gorm.Model     
  Friends []*User `gorm:"many2many:user_friends"` 
}

会创建连接表:user_friends

走定义连接池

type Person struct {
  ID        int
  Name      string
  Addresses []Address `gorm:"many2many:person_addressses;"`
}

type Address struct {
  ID   uint
  Name string
}

type PersonAddress struct {
  PersonID  int `gorm:"primaryKey"`
  AddressID int `gorm:"primaryKey"`
  CreatedAt time.Time
  DeletedAt gorm.DeletedAt
}

func (PersonAddress) BeforeCreate(db *gorm.DB) error {
  // ...
}

// 修改 Person 的 Addresses 字段的连接表为 PersonAddress
// PersonAddress 必须定义好所需的外键,否则会报错
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})