Skip to content
On this page

关联之一对一

一对一的场景下,有两种模式,一种是Belongs To(属于),一种是Has One(拥有一个)

Belongs To

belongs to可以理解为A身上打了B标签A知道B的所有信息,但是BA一无所知,可以理解为单相思自作多情,一方会在自己这边记住对方,而对方却毫不知情

Go
type Dog struct {
	ID        uint
	Name      string
	GirlGod   GirlGod
	GirlGodID uint // 外键
}

type GirlGod struct {
	ID   uint
	Name string
}

TIP

  • Dog结构体中声明了GirlGodGirlGodID,但是GirlGod结构体可以不声明关于Dog的任何信息

  • 使用DB.AutoMigrate(&Dog{})创建表时,会自动创建dogs表和girl_gods

  • 执行DB.AutoMigrate(&Dog{})会执行以下语sql
SQL
CREATE TABLE dogs
(
    id          bigint UNSIGNED AUTO_INCREMENT
        PRIMARY KEY,
    name        longtext        NULL,
    girl_god_id bigint UNSIGNED NULL,
    CONSTRAINT fk_dogs_girl_god
        FOREIGN KEY (girl_god_id) REFERENCES girl_gods (id)
);

CREATE TABLE girl_gods
(
    id   bigint UNSIGNED AUTO_INCREMENT
        PRIMARY KEY,
    name longtext NULL
);

创建关联


  • 在创建Dog的时候就指定其所属的GirlGod
Go
func dogCreate() {
	g1 := GirlGod{
		Name: "女神1号",
	}
	d1 := Dog{
		Name:    "狗哥1号",
		GirlGod: g1,
	}
	DB.Create(&d1)
}
// 下面同理
func dogCreate() {
	g2 := GirlGod{
		Name: "女神2号",
	}
	d2 := Dog{
		Name:    "狗哥2号",
		GirlGod: g2,
	}
	DB.Create(&d2)
}

执行过程如下:

  • 先创建新的DogGirlGod

  • 将新创建的Doggirl_god_id设置为新创建的GirlGodid

SQL
INSERT INTO `girl_gods` (`name`) VALUES ('女神1号') ON DUPLICATE KEY UPDATE `id`=`id`
INSERT INTO `dogs` (`name`,`girl_god_id`) VALUES ('狗哥1号',1)

Append替换关联

TIP

对于一对一(Belongs ToHas One)的场景,Append的作用就是替换关联

  • 先准备一些新的数据
Go
func prepareData() {
	// 先创建一些GirlGod数据
	girlGods := []GirlGod{
		{Name: "女神1号"},
		{Name: "女神2号"},
		{Name: "女神3号"},
		{Name: "女神4号"},
		{Name: "女神5号"},
		{Name: "女神6号"},
	}
	DB.Model(&GirlGod{}).Create(&girlGods)
	var girlGod1 GirlGod
	var girlGod2 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod1, 1)
	DB.Model(&GirlGod{}).Find(&girlGod2, 2)
	// 创建Dog数据 并关联GirlGod
	dog1 := Dog{Name: "狗哥1号", GirlGod: girlGod1}
	dog2 := Dog{Name: "狗哥2号", GirlGod: girlGod2}
	dogs := []Dog{dog1, dog2}
	DB.Model(&Dog{}).Create(&dogs)
}

得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": 1
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 2
  }
]

使用Append替换关联

  • 狗哥1号关联的女神1号替换为女神6号
Go
func operateAssociations() {
	var dog1 Dog
	DB.Model(&Dog{}).Find(&dog1, 1)
	var girlGod6 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod6, 6)
	// 将狗哥1号关联的女神1号替换为女神6号
	err := DB.Model(&dog1).Association("GirlGod").Append(&girlGod6)
	if err != nil {
		return
	}
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": 6
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 2
  }
]

Replace替换关联


不指定替换项和被替换项

  • 狗哥2号关联的女神2号替换为女神5号
Go
func operateAssociations() {
	var dog2 Dog
	DB.Model(&Dog{}).Find(&dog2, 2)
	var girlGod5 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod5, 5)
	// 将狗哥2号关联的女神2号替换为女神5号
	err := DB.Model(&dog2).Association("GirlGod").Replace(&girlGod5)
	if err != nil {
		return
	}
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": 6
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 5
  }
]

指定替换项和被替换项

  • 将狗哥2号关联的女神5号替换为女神2号
Go
func operateAssociations() {
	var dog2 Dog
	DB.Model(&Dog{}).Find(&dog2, 2)
	var girlGod5 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod5, 5)
	var girlGod2 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod2, 2)
	// 将狗哥2号关联的女神5号替换为女神2号
	err := DB.Model(&dog2).Association("GirlGod").Replace(&girlGod5, &girlGod2)
	if err != nil {
		return
	}
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": 6
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 2
  }
]

Delete删除关联

  • 删除狗哥1号关联的女神6号
Go
func operateAssociations() {
	var dog1 Dog
	DB.Model(&Dog{}).Find(&dog1, 1)
	var girlGod6 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod6, 6)
	// 删除狗哥1号关联的女神6号
	err := DB.Model(&dog1).Association("GirlGod").Delete(&girlGod6)
	if err != nil {
		return
	}
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": null
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 2
  }
]

Clear清除所有关联

  • 狗哥2号关联的女神清除
Go
func operateAssociations() {
	var dog2 Dog
	DB.Model(&Dog{}).Find(&dog2, 2)
	// 将狗哥2号关联的女神清除
	err := DB.Model(&dog2).Association("GirlGod").Clear()
	if err != nil {
		return
	}
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": null
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": null
  }
]

查询关联数据

TIP

  • 查询关联数据之前需要使用Preload加载相关数据
  • 先回归一下数据
Go
func setAssociation() {
	var dog1 Dog
	DB.Model(&Dog{}).Find(&dog1, 1)
	var dog2 Dog
	DB.Model(&Dog{}).Find(&dog2, 2)
	var girlGod6 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod6, 6)
	var girlGod5 GirlGod
	DB.Model(&GirlGod{}).Find(&girlGod5, 5)
	DB.Model(&dog1).Association("GirlGod").Append(&girlGod6)
	DB.Model(&dog2).Association("GirlGod").Append(&girlGod5)
}

此时得到的dog数据为

json
[
  {
    "id": 1,
    "name": "狗哥1号",
    "girl_god_id": 6
  },
  {
    "id": 2,
    "name": "狗哥2号",
    "girl_god_id": 5
  }
]
  • 查询关联
Go
// 查询关联
func queryAssociation() {
	var dog1 Dog
	DB.Preload("GirlGod").Find(&dog1, 1)
	fmt.Printf("dog1 is %v\n", dog1)
	// dog1 is {1 狗哥1号 {6 女神6号} 6}
}

Has One

  • Has One的结构和Belongs To的略有不同,如下
Go
type HDog struct {
	ID         uint
	Name       string
	HGirlGodID uint // 外键,就是HGirlGod.ID
}

type HGirlGod struct {
	ID   uint
	Name string
	HDog HDog
}

TIP

  • 此时执行DB.AutoMigrate(&HGirlGod{}),会自动创建h_girl_gods表,并不会自动创建h_dogs表,sql如下
sql
CREATE TABLE h_girl_gods
(
    id   bigint UNSIGNED AUTO_INCREMENT
        PRIMARY KEY,
    name longtext NULL
); 

创建数据

  • Belongs To的方式一样

  • 创建女神1号女神2号狗哥1号,并将狗哥1号女神2号创建关联

Go
// 创建女神1号和女神2号、狗哥1号,并将狗哥1号和女神2号创建关联
func createData() {
	hGirlGod1 := HGirlGod{Name: "女神1号"}
	DB.Model(&HGirlGod{}).Create(&hGirlGod1)
	hDog1 := HDog{Name: "狗哥1号"}
	hGirlGod2 := HGirlGod{Name: "女神2号", HDog: hDog1}
	DB.Model(&HGirlGod{}).Create(&hGirlGod2)
}

Append替换数据

  • 狗哥1号关联女神2号替换为狗哥1号关联女神1号
Go
func operateAssociation() {
	var hDog1 HDog
	DB.Model(&HDog{}).Find(&hDog1, 1)
	var hGirlGod1 HGirlGod
	DB.Model(&HGirlGod{}).Find(&hGirlGod1, 1)
	// 将女神1号关联的HDog替换为hDog1(狗哥1号)
	DB.Model(&hGirlGod1).Association("HDog").Append(&hDog1)
}

sql如下:

sql
# 设置狗哥1号的关联女神ID为女神1号的ID
INSERT INTO `h_dogs` (`name`,`h_girl_god_id`,`id`) VALUES ('狗哥1号',1,1) ON DUPLICATE KEY UPDATE `h_girl_god_id`=VALUES(`h_girl_god_id`)
# 设置其他所有ID不为1的狗哥的女神ID为null
UPDATE `h_dogs` SET `h_girl_god_id`=NULL WHERE `h_dogs`.`id` <> 1 AND `h_dogs`.`h_girl_god_id` = 1

Replace替换数据

  • 狗哥1号女神1号关联的关联替换为女神2号
Go
func operateAssociation() {
	var hDog1 HDog
	DB.Model(&HDog{}).Find(&hDog1, 1)
	var hGirlGod2 HGirlGod
	DB.Model(&HGirlGod{}).Find(&hGirlGod2, 2)
	// 将女神2号关联的HDog替换为hDog1(狗哥1号)
	DB.Model(&hGirlGod2).Association("HDog").Replace(&hDog1)
}

DeleteClear

DeleteClear就和上面的相同,只是方法替换了一下