golang学习笔记(13)-gorm多态和引用关联标签

gorm多态,引用和关联标签

目录

  • gorm多态,引用和关联标签
    • 准备工作
    • 多态
    • 引用和关联标签
      • 关联标签
      • 引用
        • 除多对多
        • 多对多
        • 意外发现

准备工作

建立数据库连接

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"log"
)

var db *gorm.DB

func OpenDB() {
	dsn := "root:adss123@tcp(127.0.0.1:3306)/go_db?charset=utf8mb4&parseTime=True&loc=Local"
	res, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	db = res
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("成功:%v\n", db)
}

多态

由于多态的结构体的数据不可以同时被多表拥有,所以多态关系只存在于hasOne和hasMany中。
在上述两种关系的应用中,时常出现多表添加关系,每次添加一张表,都需要重新修改表的结构,增加外键,导致维护表的工作量增加,此时需要多态的概念来简化工作。
GORM 为 has one 和 has many 提供了多态关联支持,它会将拥有者实体的表名、主键都保存到多态类型的字段中。

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

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

db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs"), ("toy2","1","dogs")

可以使用标签 polymorphicValue 来更改多态类型的值,例如:

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

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

db.Create(&Dog{Name: "dog1", Toy: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master"), ("toy2","1","master")

实验案例如下
当想记录男孩女孩玩具之间的关系时,关联关系是,男孩和女孩是两个不同的模型,他们都与玩具存在一对一关系。则常规建表方法为:

type Boys struct {
	gorm.Model
	BoyName string
	Toy     Toys
}
type Girls struct {
	gorm.Model
	GirlName string
	Toy      Toys
}
type Toys struct {
	gorm.Model
	ToyName string
	BoysID  uint
	GirlsID uint
}

当使用多态,则可简化为:

type Boys struct {
	gorm.Model
	BoyName string
	Toy     Toys `gorm:"polymorphic:Owner;"`
}
type Girls struct {
	gorm.Model
	GirlName string
	Toy      Toys `gorm:"polymorphic:Owner;"`
}
type Toys struct {
	gorm.Model
	ToyName   string
	OwnerID   uint
	OwnerType string
}

表结构为:
toys表
在这里插入图片描述
boys表
在这里插入图片描述
girls表
在这里插入图片描述

现在boy与girl各创建一条记录,观察多态的变化。
创建记录代码:

func InsertPoly() {
	OpenDB()
	boy := &Boys{BoyName: "小胖", Toy: Toys{
		ToyName: "挖掘机",
	}}
	girl := &Girls{GirlName: "小美", Toy: Toys{ToyName: "小风车"}}
	db.Create(boy)
	db.Create(girl)
}

boy与girl表没有特殊变化,正常记录,不做展示。
toy表:
golang学习笔记(13)-gorm多态和引用关联标签_第1张图片
此时已经创建联系,owner_type记录的表名字,owner_id记录的玩具拥有者。
尝试查询:

func QueryPoly() {
	OpenDB()
	boy := &Boys{}
	db.Preload("Toy").Find(boy, 1)
	fmt.Println(boy)
}

在这里插入图片描述
查询成功。
尝试修改关联:
在boys表中多建立一个记录
golang学习笔记(13)-gorm多态和引用关联标签_第2张图片
讲小胖和玩具的关联,替换到小恶霸上。

func QueryPoly() {
	OpenDB()
	boy := &Boys{Model: gorm.Model{
		ID: 2,
	}}
	db.Model(boy).Association("Toy").Replace(&Toys{Model: gorm.Model{ID: 1}})
	fmt.Println(boy)
}

结果
在这里插入图片描述
替换成功。

引用和关联标签

关联标签

标签 描述
foreignKey 指定当前模型的列作为连接表的外键
references 指定引用表的列名,其将被映射为连接表外键
polymorphic 指定多态类型,比如模型名
polymorphicValue 指定多态值、默认表名
many2many 指定连接表表名
joinForeignKey 指定连接表的外键列名,其将被映射到当前表
joinReferences 指定连接表的外键列名,其将被映射到引用表
constraint 关系约束,例如:OnUpdate、OnDelete

多态在上文已经学习,现着重学习外键与外键引用

引用

除多对多

在has one的关联关系下,常规建表副表会定义一个与主表名称相同的XXXID来作为外键,所引用的值是主表的主键。例如:

type CardUser struct {
	gorm.Model
	CreditCard CreditCard
}

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

接下来重写外键,让外键从CardUserID改为NewWaijian:

type CardUser struct {
   gorm.Model
   name string
   CreditCard CreditCard `gorm:"foreignKey:NewWaijian""`
}

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

在这里插入图片描述
现在增加一条记录

func InserteHasone() {
	OpenDB()
	user := &CardUser{CreditCard: CreditCard{Number: "123"}}
	db.Create(user)
}

在这里插入图片描述
发现引用值依然是主表的主键。
现在想把引用改为User的名称。

type CardUser struct {
	gorm.Model
	Name       string     `gorm:"type:varchar(100);index"`
	CreditCard CreditCard `gorm:"foreignKey:NewWaijian;references:Name;"`
}

type CreditCard struct {
	gorm.Model
	Number     string
	NewWaijian string
}

实验时出现Error 1170: BLOB/TEXT column ‘new_waijian’ used in key specification without a key length错误,错误发生的原因是因为MySQL只能将BLOB/TEXT类型字段设置索引为BLOB/TEXT数据的钱N个字符,因此错误常常发生在字段被定义为TEXT/BLOB类型或者和TEXT/BLOB同质的数据类型,如TINYTEXT,MEDIUMTEXT,LONGTEXT。

解决方案是将unique限制和索引从TEXT/BLOB字段中移除,或者是设置另一个字段为主键,如果你不愿意这样做并且想在TEXT/BLOB上加限制,那么你可以尝试将这个字段更改为VARCHAR类型,同时给他一个限制长度,默认VARCHAR最多可以限定在255个字符,并且限制要在声明类型的右边指明,如VARCHAR(200)将会限制仅仅200个字符


创建一条记录

func InserteHasone() {
	OpenDB()
	user := &CardUser{Name: "ylj", CreditCard: CreditCard{Number: "123"}}
	db.Create(user)
}

在这里插入图片描述
这时外键引用的则是主键里的名称值。

多对多

在多对多关系中,由于连接关系储存在连接表中,所以外键和引用值的参数,与其他关系不同。
首先建立表

type Students struct {
	gorm.Model
	Name    string    `gorm:"type:varchar(100);index"`
	Classes []Classes `gorm:"many2many:students_classes;foreignKey:Name;references:Name;"`
}

type Classes struct {
	gorm.Model
	Name     string     `gorm:"type:varchar(100);index"`
	Students []Students `gorm:"many2many:students_classes;"`
}

这里故意不区分两张表内的Name,观察重写操作。
创建一条记录,并关联。

func InsertMany2Many() {
	OpenDB()
	class := &Classes{Name: "math"}
	stu := &Students{Name: "stu1"}
	db.Create(class)
	db.Create(stu)
	db.Model(stu).Association("Classes").Append(class)
}

golang学习笔记(13)-gorm多态和引用关联标签_第3张图片
操作成功,外键为students_name,引用值为classes_name

再将重写标签,写在classes结构体内

type Students struct {
	gorm.Model
	Name    string    `gorm:"type:varchar(100);index"`
	Classes []Classes `gorm:"many2many:students_classes;"`
}

type Classes struct {
	gorm.Model
	Name     string     `gorm:"type:varchar(100);index"`
	Students []Students `gorm:"many2many:students_classes;foreignKey:Name;references:Name;"`
}

golang学习笔记(13)-gorm多态和引用关联标签_第4张图片
发现重写操作失败


现在将关联表名字反转为classes——students
发现创建的表依旧存在问题。
在这里插入图片描述


最后实验多次,发现是创建函数出现问题。

func CreateMany2Many() {
	OpenDB()
	db.AutoMigrate(&Classes{})
	InsertMany2Many()

}

改为

func CreateMany2Many() {
	OpenDB()
	db.AutoMigrate(&Students{})
	InsertMany2Many()

}

后实验成功。
在这里插入图片描述
对于本次实验目的得出结论一,外键一般只能重写为标签所在的结构体里的字段,引用指向关系表内的字段,当外键字段与引用字段同名时,也遵循这个规则,外键指向标签所在结构体内的name,引用指向关系表内的name。

意外发现

通常认为,存在外键的表为副表,除开belongsto关系时,通常建立主表的时候,会自动创建副表,此实验也相同,因为一对一与一对多关系中主表通常嵌套副表结构体,所以会自动创建。而本次实验优先创立副表,虽然包括连接表在内的三张表都成功创建,但是由于优先创立副表的关系,重写操作均失效,可能原因是,由于many2many关系中,两个模型相互嵌套,无论优先创建那个表,都会自动创建另一张表,但副表建立时,主表还未建立,副表内的重写应用标签无法对应上主表内的字段而无法运行,所以略过了标签,导致未执行重写操作,解决办法,养成优先创立主表的习惯。

你可能感兴趣的:(golang,学习,数据库)