package gormExample import ( "fmt" "gorm.io/gorm/clause" "log" ) func StdAssocModel() { // 利用migrate创建表 // 以及多对多的关联表 // 以及外键约束 if err := DB.AutoMigrate(&Author{}, &Essay{}, &Tag{}, &EssayMate{}); err != nil { log.Fatalln(err) } // CREATE TABLE `msb_author` ( // `id` bigint unsigned NOT NULL AUTO_INCREMENT, // `created_at` datetime(3) DEFAULT NULL, // `updated_at` datetime(3) DEFAULT NULL, // `deleted_at` datetime(3) DEFAULT NULL, // `status` bigint DEFAULT NULL, // `name` longtext, // `email` longtext, // PRIMARY KEY (`id`), // KEY `idx_msb_author_deleted_at` (`deleted_at`) //) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci // CREATE TABLE `msb_essay` ( // `id` bigint unsigned NOT NULL AUTO_INCREMENT, // `created_at` datetime(3) DEFAULT NULL, // `updated_at` datetime(3) DEFAULT NULL, // `deleted_at` datetime(3) DEFAULT NULL, // `subject` longtext, // `content` longtext, // `author_id` bigint unsigned DEFAULT NULL, // PRIMARY KEY (`id`), // KEY `idx_msb_essay_deleted_at` (`deleted_at`), // KEY `fk_msb_author_essays` (`author_id`), // CONSTRAINT `fk_msb_author_essays` FOREIGN KEY (`author_id`) REFERENCES `msb_author` (`id`) //) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci // CREATE TABLE `msb_essay_mate` ( // `id` bigint unsigned NOT NULL AUTO_INCREMENT, // `created_at` datetime(3) DEFAULT NULL, // `updated_at` datetime(3) DEFAULT NULL, // `deleted_at` datetime(3) DEFAULT NULL, // `keyword` longtext, // `description` longtext, // `essay_id` bigint unsigned DEFAULT NULL, // PRIMARY KEY (`id`), // KEY `idx_msb_essay_mate_deleted_at` (`deleted_at`), // KEY `fk_msb_essay_essay_mate` (`essay_id`), // CONSTRAINT `fk_msb_essay_essay_mate` FOREIGN KEY (`essay_id`) REFERENCES `msb_essay` (`id`) //) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci // CREATE TABLE `msb_tag` ( // `id` bigint unsigned NOT NULL AUTO_INCREMENT, // `created_at` datetime(3) DEFAULT NULL, // `updated_at` datetime(3) DEFAULT NULL, // `deleted_at` datetime(3) DEFAULT NULL, // `title` longtext, // PRIMARY KEY (`id`), // KEY `idx_msb_tag_deleted_at` (`deleted_at`) //) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci // CREATE TABLE `msb_essay_tag` ( // `tag_id` bigint unsigned NOT NULL, // `essay_id` bigint unsigned NOT NULL, // PRIMARY KEY (`tag_id`,`essay_id`), // KEY `fk_msb_essay_tag_essay` (`essay_id`), // CONSTRAINT `fk_msb_essay_tag_essay` FOREIGN KEY (`essay_id`) REFERENCES `msb_essay` (`id`), // CONSTRAINT `fk_msb_essay_tag_tag` FOREIGN KEY (`tag_id`) REFERENCES `msb_tag` (`id`) //) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci log.Println("migrate successful") } // 添加关联 func AssocAppend() { // A:一对多的关系, Author 1:n Essay // 创建测试数据 var a Author a.Name = "一位作者" if err := DB.Create(&a).Error; err != nil { log.Println(err) } log.Println("a:", a.ID) var e1, e2 Essay e1.Subject = "一篇内容" //e1.AuthorID = a.ID e2.Subject = "另一篇内容" if err := DB.Create([]*Essay{&e1, &e2}).Error; err != nil { log.Println(err) } log.Println("e1, e2: ", e1.ID, e2.ID) // 添加关联 if err := DB.Model(&a).Association("Essays").Append([]Essay{e1}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) // 基于当前的基础上,添加关联 if err := DB.Model(&a).Association("Essays").Append([]Essay{e2}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) // 添加后,a模型对象的Essays字段,自动包含了关联的Essay模型 //fmt.Println(a.Essays) // B: Essay M:N TAg var t1, t2, t3 Tag t1.Title = "Go" t2.Title = "GORM" t3.Title = "Ma" if err := DB.Create([]*Tag{&t1, &t2, &t3}).Error; err != nil { log.Println(err) } log.Println("t1, t2, t3: ", t1.ID, t2.ID, t3.ID) // e1 t1, t3 // e2 t1, t2, t3 if err := DB.Model(&e1).Association("Tags").Append([]Tag{t1, t3}); err != nil { log.Println(err) } if err := DB.Model(&e2).Association("Tags").Append([]Tag{t1, t2, t3}); err != nil { log.Println(err) } // 关联表查看 // mysql> select * from msb_essay_tag; //+--------+----------+ //| tag_id | essay_id | //+--------+----------+ //| 1 | 12 | //| 3 | 12 | //| 1 | 13 | //| 2 | 13 | //| 3 | 13 | //+--------+----------+ // C, Belongs To. Essay N:1 Author var e3 Essay e3.Subject = "第三篇内容" if err := DB.Create([]*Essay{&e3}).Error; err != nil { log.Println(err) } log.Println("e3: ", e3.ID) log.Println(e3.Author) // 关联 if err := DB.Model(&e3).Association("Author").Append(&a); err != nil { log.Println(err) } log.Println(e3.Author.ID) // 对一的关联,会导致关联被更新 var a2 Author a2.Name = "另一位作者" if err := DB.Create(&a2).Error; err != nil { log.Println(err) } log.Println("a2:", a2.ID) if err := DB.Model(&e3).Association("Author").Append(&a2); err != nil { log.Println(err) } log.Println(e3.Author.ID) } func AssocReplace() { // A. 替换 // 创建测试数据 var a Author a.Name = "一位作者" if err := DB.Create(&a).Error; err != nil { log.Println(err) } log.Println("a:", a.ID) var e1, e2, e3 Essay e1.Subject = "一篇内容" e2.Subject = "另一篇内容" e3.Subject = "第三篇内容" if err := DB.Create([]*Essay{&e1, &e2, &e3}).Error; err != nil { log.Println(err) } log.Println("e1, e2, e3: ", e1.ID, e2.ID, e3.ID) // 添加关联 if err := DB.Model(&a).Association("Essays").Replace([]Essay{e1, e3}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) // 基于当前的基础上,添加关联 if err := DB.Model(&a).Association("Essays").Replace([]Essay{e2, e3}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) } func AssocDelete() { // B. 删除,外键的 // 创建测试数据 var a Author a.Name = "一位作者" if err := DB.Create(&a).Error; err != nil { log.Println(err) } log.Println("a:", a.ID) var e1, e2, e3 Essay e1.Subject = "一篇内容" e2.Subject = "另一篇内容" e3.Subject = "第三篇内容" if err := DB.Create([]*Essay{&e1, &e2, &e3}).Error; err != nil { log.Println(err) } log.Println("e1, e2, e3: ", e1.ID, e2.ID, e3.ID) // 添加关联 if err := DB.Model(&a).Association("Essays").Replace([]Essay{e1, e2, e3}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) if err := DB.Model(&a).Association("Essays").Delete([]Essay{e1, e3}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) fmt.Println("------------------------") // B. 删除,多对多,关联表 var t1, t2, t3 Tag t1.Title = "Go" t2.Title = "GORM" t3.Title = "Ma" if err := DB.Create([]*Tag{&t1, &t2, &t3}).Error; err != nil { log.Println(err) } log.Println("t1, t2, t3: ", t1.ID, t2.ID, t3.ID) // e1 t1, t3 // e2 t1, t2, t3 if err := DB.Model(&e1).Association("Tags").Append([]Tag{t1, t2, t3}); err != nil { log.Println(err) } fmt.Println(len(e1.Tags)) if err := DB.Model(&e1).Association("Tags").Delete([]Tag{t1, t3}); err != nil { log.Println(err) } fmt.Println(len(e1.Tags)) // C. 清空关联 if err := DB.Model(&e1).Association("Tags").Clear(); err != nil { log.Println(err) } fmt.Println(len(e1.Tags)) } func AssocFind() { // e := Essay{} DB.First(&e, 18) // 查询关联的tags //var ts []Tag if err := DB.Model(&e).Association("Tags").Find(&e.Tags); err != nil { log.Println(err) } log.Println(e.Tags) // 子句,要写在Association()方法前面 if err := DB.Model(&e). Where("tag_id > ?", 7). Order("tag_id DESC"). Association("Tags").Find(&e.Tags); err != nil { log.Println(err) } log.Println(e.Tags) // 查询关联的模型的数量 count := DB.Model(&e).Association("Tags").Count() log.Println("count:", count) } func AssocSave() { var t1 Tag DB.First(&t1, 10) e := Essay{ Subject: "一个组合的Save", Author: Author{Name: "马士兵"}, Tags: []Tag{ t1, {Title: "Ma"}, {Title: "GORM"}, }, } if err := DB.Save(&e).Error; err != nil { log.Println(err) } log.Printf("%+v\n", e) } // Preload func AssocPreload() { // A.直接一步查询Author对应的Essays a := Author{} if err := DB. Preload("Essays"). First(&a, 1).Error; err != nil { log.Fatalln(err) } // [3.840ms] [rows:2] SELECT * FROM `msb_essay` WHERE `msb_essay`.`author_id` = 1 AND `msb_essay`.`deleted_at` IS NULL // [13.014ms] [rows:1] SELECT * FROM `msb_author` WHERE `msb_author`.`id` = 1 AND `msb_author`.`deleted_at` IS NULL ORDER BY `msb_author`.`id` LIMIT 1 log.Println(a.Essays) log.Println("--------------------") // B.支持条件过滤 if err := DB. Preload("Essays", "id IN ?", []uint{2, 3, 4}). First(&a, 1).Error; err != nil { log.Fatalln(err) } // [3.217ms] [rows:1] SELECT * FROM `msb_essay` WHERE `msb_essay`.`author_id` = 1 AND id IN (2,3,4) AND `msb_essay`.`deleted_at` IS NULL log.Println(a.Essays) log.Println("-----------------------") // C. 支持多次链式调用,同时预加载多个关联 e := Essay{} if err := DB. //Preload("Author"). //Preload("EssayMate"). //Preload("Tags"). Preload(clause.Associations). First(&e, 1).Error; err != nil { log.Fatalln(err) } log.Println(e) // [2.776ms] [rows:1] SELECT * FROM `msb_author` WHERE `msb_author`.`id` = 1 AND `msb_author`.`deleted_at` IS NULL // [10.398ms] [rows:0] SELECT * FROM `msb_essay_mate` WHERE `msb_essay_mate`.`essay_id` = 1 AND `msb_essay_mate`.`deleted_at` IS NULL // [3.260ms] [rows:2] SELECT * FROM `msb_essay_tag` WHERE `msb_essay_tag`.`essay_id` = 1 // [3.264ms] [rows:2] SELECT * FROM `msb_tag` WHERE `msb_tag`.`id` IN (1,3) AND `msb_tag`.`deleted_at` IS NULL // [28.067ms] [rows:1] SELECT * FROM `msb_essay` WHERE `msb_essay`.`id` = 1 AND `msb_essay`.`deleted_at` IS NULL ORDER BY `msb_essay`.`id` LIMIT 1 } // 多级 func AssocLevelPreload() { a := Author{} if err := DB. //Preload("Essays"). // 多级关联 Preload("Essays.Tags"). First(&a, 1).Error; err != nil { log.Fatalln(err) } // [3.843ms] [rows:5] SELECT * FROM `msb_essay_tag` WHERE `msb_essay_tag`.`essay_id` IN (1,2) // [3.284ms] [rows:3] SELECT * FROM `msb_tag` WHERE `msb_tag`.`id` IN (1,3,2) AND `msb_tag`.`deleted_at` IS NULL // [10.396ms] [rows:2] SELECT * FROM `msb_essay` WHERE `msb_essay`.`author_id` = 1 AND `msb_essay`.`deleted_at` IS NULL // [17.609ms] [rows:1] SELECT * FROM `msb_author` WHERE `msb_author`.`id` = 1 AND `msb_author`.`deleted_at` IS NULL ORDER BY `msb_author`.`id` LIMIT 1 log.Println(a.Essays[0].Tags) log.Println(a.Essays[1].Tags) } func AssocOperate() { // 创建测试数据 var a Author a.Name = "一位作者" if err := DB.Create(&a).Error; err != nil { log.Println(err) } log.Println("a:", a.ID) var e1, e2 Essay e1.Subject = "一篇内容" //e1.AuthorID = a.ID e2.Subject = "另一篇内容" if err := DB.Create([]*Essay{&e1, &e2}).Error; err != nil { log.Println(err) } log.Println("e1, e2: ", e1.ID, e2.ID) // 基于当前的基础上,添加关联 if err := DB.Model(&a).Association("Essays").Append([]Essay{e1, e2}); err != nil { log.Println(err) } fmt.Println(len(a.Essays)) // 删除a,注意essays的处理 if err := DB.Unscoped().Delete(&a).Error; err != nil { log.Fatalln(err) } }