golang编程语言操作GORM快速上手篇

一.GORM概述

1.学习方法

3w1h指What、Why、Where、How。

学技术也有方法可言,比如学习一个新的技术gorm:
	- 新手期:
		先到百度搜索几篇基本的用法案例,粘贴过来测试一下,测试通了说明你对grom有一个基础的了解。
	- 筑基期:
		到官方系统的整理,最好整理成博客,以便于以后用的时候方便查询,但这只是学会了基础语法。
	- 结丹期:
		如何将学到的东西融入到项目中,最好给自己找几个需求,然后练习实现,从而不断让自己的技术沉淀。
		如果没有案例的话,可以在GitHub或者gitee上找一些开源项目模仿跑通。
		如果你真的能做到这个阶段,保守估计,想必你的薪资应该不会低于: 35K+
	- 化神期:
		读源码和实现原理,参加过大厂面试的小伙伴应该都知道,他们很喜欢为什么问底层原理,其实了解底层原理有助于咱们做故障排查。
		比如公司要实现一个CMDB系统,你如果了解类似相关产品的实现方案的底层原理,想必你动手写应该不会毫无头绪。

2.orm是一种术语而不是软件

- orm英文全称object relational mapping,就是 对象映射关系 程序

- 简单来说类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的

- 为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系

- 这样我们直接使用编程语言的对象模型进行操作数据库就可以了,而不用直接使用sql语言

3.什么是grom

gorm是一个使用Go语言编写的ORM框架。它文档齐全,对开发者友好,支持主流数据库。

gorm官方中文文档:https://gorm.io/zh_CN/docs/

二.GORM基本使用

1.安装gorm

go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
go get -u gorm.io/driver/mysql

2.创建数据库

	1.创建数据库和表信息
CREATE DATABASE yinzhengjie CHARSET utf8mb4;
USE yinzhengjie;
CREATE TABLE users(
    id BIGINT(20) PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID',
    username VARCHAR(255) NOT NULL COMMENT '用户名',
    password VARCHAR(255) NOT NULL COMMENT '密码',
    status ENUM('active', 'inactive', 'deleted') NOT NULL DEFAULT 'active' COMMENT '用户状态'
);


	2.查看表结构信息
mysql> DESC users;
+----------+-------------------------------------+------+-----+---------+----------------+
| Field    | Type                                | Null | Key | Default | Extra          |
+----------+-------------------------------------+------+-----+---------+----------------+
| id       | bigint                              | NO   | PRI | NULL    | auto_increment |
| username | varchar(255)                        | NO   |     | NULL    |                |
| password | varchar(255)                        | NO   |     | NULL    |                |
| status   | enum('active','inactive','deleted') | NO   |     | active  |                |
+----------+-------------------------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> 


	3.配置远程用户授权访问
CREATE USER IF NOT EXISTS admin IDENTIFIED WITH mysql_native_password BY 'yinzhengjie';
GRANT ALL ON yinzhengjie.* TO admin;




	4.查看权限验证
mysql> SHOW GRANTS FOR 'admin';
+--------------------------------------------------------+
| Grants for admin@%                                     |
+--------------------------------------------------------+
| GRANT USAGE ON *.* TO `admin`@`%`                      |
| GRANT ALL PRIVILEGES ON `yinzhengjie`.* TO `admin`@`%` |
+--------------------------------------------------------+
2 rows in set (0.00 sec)

mysql> 


	5.访问测试
[root@yinzhengjie ~]# mysql -uadmin -pyinzhengjie -h 10.0.0.20 -e 'SHOW DATABASES;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| yinzhengjie        |
+--------------------+
[root@yinzhengjie ~]# 

3.连接数据库创建表

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type UserInfo struct {
	/**
	json:"id"
		在json格式解析时字段名称为id。
	grom:"primary_key"
		对于grom框架而言,声明该字段为主键。

	*/
	Id       int64 `json:"id" grom:"primary_key"`
	Username string
	Password string
}

func main() {
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Printf("连接数据库出错: %v\n", err)
		panic(err) // 如果连接出错,就直接抛出异常!
	}

	fmt.Printf("数据库连接成功: %v\n", db)

	/**
	使用AutoMigrate自动创建表结构.

	参考连接:
		https://gorm.io/zh_CN/docs/migration.html

	这个语句慎用,因为其创建的表的数据类型可能比我们预期要给的大:
		mysql> desc user_infos;
		+----------+----------+------+-----+---------+----------------+
		| Field    | Type     | Null | Key | Default | Extra          |
		+----------+----------+------+-----+---------+----------------+
		| id       | bigint   | NO   | PRI | NULL    | auto_increment |
		| username | longtext | YES  |     | NULL    |                |
		| password | longtext | YES  |     | NULL    |                |
		+----------+----------+------+-----+---------+----------------+
		3 rows in set (0.00 sec)

		mysql>
	*/
	err = db.AutoMigrate(UserInfo{})
	if err != nil {
		panic(fmt.Sprintf("表创建失败,%v\n", err))
	}
	fmt.Println("数据表创建成功!")

}

4.单表的增删改查

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type UserInfo struct {
	Id       int64 `json:"id" grom:"primary_key"`
	Username string
	Password string
}

func main() {
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Printf("连接数据库出错: %v\n", err)
		panic(err) // 如果连接出错,就直接抛出异常!
	}

	err = db.AutoMigrate(UserInfo{})

	// 1.向数据库添加数据,注意多次执行会创建多个用户哟,而且会自动基于ID自增,小心造成数据的重复创建哟!
	db.Create(&UserInfo{
		Username: "JasonYin",
		Password: "yinzhengjie",
	})

	db.Create(&UserInfo{
		Username: "root",
		Password: "yinzhengjie",
	})

	db.Create(&UserInfo{
		Username: "root",
		Password: "123",
	})

	// 2.修改表中的某一个字段
	db.Model(UserInfo{
		Id: 1,
	}).Update("password", "123")

	// 4. 过滤查询数据单条数据
	u1 := UserInfo{Username: "root"}
	db.Where("username= ?", u1.Username).First(&u1)
	fmt.Printf("u1 ---> %#v\n", u1)

	// 5. 过滤查询数据多条数据
	u2 := UserInfo{Username: "root"}
	u2_all_user := []UserInfo{}
	db.Where("username= ?", u2.Username).Find(&u2_all_user)
	fmt.Printf("u2---> %#v\n", u2_all_user)

	// 6.查看所有数据
	users := []UserInfo{} // 定义一个UserInfo结构体切片来接收数据
	db.Find(&users)
	fmt.Printf("all ---> %#v\n", users)

	// 7.删除数据
	// 7.1 基于主键id删除,比如删除id为1的数据
	db.Delete(&UserInfo{Id: 1})
	// 7.2 基于条件删除,比如基于username为root的数据进行删除
	db.Where("username =?", "root").Delete(&UserInfo{})
}

5.数据库的模型定义

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

type User struct {
	/**
	声明 model时,tag是可选的,GORM支持以下tag。
		column:
			指定db列名
		type:
			列数据类型,推荐使用兼容性好的通用类型,
			例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,
			例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。
			在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
		serializer:
			指定将数据序列化或反序列化到数据库中的序列化器, 例如: serializer:json/gob/unixtime
		size:
			定义列数据类型的大小或长度,例如 size: 256
		primaryKey:
			将列定义为主键
		unique:
			将列定义为唯一键
		default:
			定义列的默认值
		precision:
			指定列的精度
		scale:
			指定列大小
		not null:
			指定列为NOT NULL
		autoIncrement:
			指定列为自动增长
		autoIncrementIncrement:
			自动步长,控制连续记录之间的间隔
		embedded:
			嵌套字段
		embeddedPrefix:
			嵌入字段的列名前缀
		autoCreateTime:
			创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
		autoUpdateTime:
			创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
		index:
			根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
		uniqueIndex:
			与 index 相同,但创建的是唯一索引
		check:
			创建检查约束,例如 check:age > 13,查看 约束 获取详情
		<-:
			设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
		->:
			设置字段读的权限,->:false 无读权限
		-:
			忽略该字段,- 表示无读写,-:migration 表示无迁移权限,-:all 表示无读写迁移权限
		comment:
			迁移时为字段添加注释

	tag名大小写不敏感,但建议使用camelCase风格。

	更多资料请参考官网:
		https://gorm.io/zh_CN/docs/models.html
	*/
	Id           int64      `gorm:"primary_key" json:"id"`
	Name         string     // 默认字符串对应的是数据库的longtext文本类型
	CreatedAt    *time.Time `json:"createdAt" gorm:"column:create_at"`
	Email        string     `gorm:"type:varchar(255);unique_index"` // 使用type字段定义类型,并设置为唯一索引
	Role         string     `gorm:"size:255"`                       // 设置字段的大小为255个字节
	MemberNumber *string    `gorm:"unique;not null"`                // 设置memberNumber 字段唯一且不为空
	Num          int        `gorm:"AUTO_INCREMENT"`                 // 设置 Num字段自增
	Address      string     `gorm:"index:addr"`                     // 给Address 创建一个名字是 `addr`的索引
	IgnoreMe     int        `gorm:"-"`                              // 忽略这个字段
}

func main() {
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	// 创建表
	err := db.AutoMigrate(User{})
	if err != nil {
		panic(err)
	}
}

三.一对多关联查询

1.has many 一对多概述

has many关联就是创建一个和另一个模型的一对多关系。

例如: 每一个用户都有多张信用卡,这样就是生活中一个简单的一对多关系。


定义关系:
type User struct {
    // gorm.Model是gorm内置的模块,已经定义好了相关的结构体咱们可以直接引用
	gorm.Model
	CreditCard []CreditCard
}

type CreditCard struct {
	gorm.Model
	Number string
	// 默认会在CreditCard表中生成UserID字段作为User表关联的外键ID
	UserID uint
}



推荐阅读:
	https://gorm.io/zh_CN/docs/has_many.html

2.外键

- 为了定义一对多关系, 外键是必须存在的,默认外键的名字是 所有者类型的名字加上它的主键

- 就像上面的例子,为了定义一个属于User的模型,外键就应该为UserID 。

- 使用其他的字段名作为外键, 你可以通过"foreignkey"来定制它,


举个例子:
type User struct {
	gorm.Model
	CreditCard []CreditCard `gorm:"foreignkey:UserRef"`
}

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

3.外键关联

- GORM通常使用拥有者的主键作为外键的值。 对于上面的例子,它是User的ID字段。
- 为user添加creditcard 时,GORM 会将user的ID字段保存到creditcard的UserID 字段。
- 同样的,您也可以使用标签 references 来更改它。


举个例子:
type User struct {
	gorm.Model
	MemberNumber string
	CreditCards  []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
}

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

4.外键约束

你可以通过为标签 constraint 配置 OnUpdate、OnDelete 实现外键约束,在使用 GORM 进行迁移时它会被创建.

举个例子:
type User struct {
  gorm.Model
  CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}

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

5.一对多案例

package main

import (
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 2.建立一对多关系
type User struct {
	gorm.Model
	Username string `json:"username gorm:"column:username"`
	// 添加外键关联,只要该字段是一个CreditCard的切片,会自动和CreditCard模型建立外键关联。
	CreditCards []CreditCard
}

type CreditCard struct {
	gorm.Model
	Number string
	// 这个就是User表关联的外键,表示基于User表的ID主键(primary key)字段进行关联。这个是gorm的规定【结构体 + 主键】
	UserID uint
}

func main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	fmt.Println(db)

	// 3.创建表结构
	//db.AutoMigrate(User{}, CreditCard{})

	/**
	4.创建一对多

	创建后记得查看数据库信息:
	mysql> SHOW TABLES;
	+-----------------------+
	| Tables_in_yinzhengjie |
	+-----------------------+
	| credit_cards          |
	| users                 |
	+-----------------------+
	2 rows in set (0.00 sec)

	mysql>
	mysql> SELECT * FROM users;
	+----+-------------------------+-------------------------+------------+-----------+
	| id | created_at              | updated_at              | deleted_at | username  |
	+----+-------------------------+-------------------------+------------+-----------+
	|  1 | 2023-11-20 23:48:17.561 | 2023-11-20 23:48:17.561 | NULL       | Jason Yin |
	+----+-------------------------+-------------------------+------------+-----------+
	1 row in set (0.01 sec)

	mysql>
	mysql> SELECT * FROM credit_cards;
	+----+-------------------------+-------------------------+------------+--------+---------+
	| id | created_at              | updated_at              | deleted_at | number | user_id |
	+----+-------------------------+-------------------------+------------+--------+---------+
	|  1 | 2023-11-20 23:48:17.574 | 2023-11-20 23:48:17.574 | NULL       | 1001   |       1 |
	|  2 | 2023-11-20 23:48:17.574 | 2023-11-20 23:48:17.574 | NULL       | 1002   |       1 |
	+----+-------------------------+-------------------------+------------+--------+---------+
	2 rows in set (0.00 sec)

	mysql>

	*/
	//user := User{
	//	Username: "Jason Yin",
	//	CreditCards: []CreditCard{
	//		{Number: "1001"},
	//		{Number: "1002"},
	//	},
	//}
	//
	//db.Create(&user)

	// 5.为已存在的用户添加信用卡
	u := User{
		Username: "Jason Yin",
	}
	db.First(&u) // 查找返回的第一条记录

	// 使用Association方法基于指定"CreditCards"关联外键字段找到关联关系,并使用Append方法追加信息。
	db.Model(&u).Association("CreditCards").Append([]CreditCard{
		{Number: "1003"},
		{Number: "1004"},
	})

	/**
		添加成功后,再次查看表信息


		mysql> SHOW TABLES;
		+-----------------------+
		| Tables_in_yinzhengjie |
		+-----------------------+
		| credit_cards          |
		| users                 |
		+-----------------------+
		2 rows in set (0.00 sec)

		mysql>
	    mysql> SELECT * FROM users;
		+----+-------------------------+-------------------------+------------+-----------+
		| id | created_at              | updated_at              | deleted_at | username  |
		+----+-------------------------+-------------------------+------------+-----------+
		|  1 | 2023-11-20 23:48:17.561 | 2023-11-21 00:00:12.988 | NULL       | Jason Yin |
		+----+-------------------------+-------------------------+------------+-----------+
		1 row in set (0.00 sec)

		mysql>
		mysql> SELECT * FROM credit_cards;
		+----+-------------------------+-------------------------+------------+--------+---------+
		| id | created_at              | updated_at              | deleted_at | number | user_id |
		+----+-------------------------+-------------------------+------------+--------+---------+
		|  1 | 2023-11-20 23:48:17.574 | 2023-11-20 23:48:17.574 | NULL       | 1001   |       1 |
		|  2 | 2023-11-20 23:48:17.574 | 2023-11-20 23:48:17.574 | NULL       | 1002   |       1 |
		|  3 | 2023-11-21 00:00:12.991 | 2023-11-21 00:00:12.991 | NULL       | 1003   |       1 |
		|  4 | 2023-11-21 00:00:12.991 | 2023-11-21 00:00:12.991 | NULL       | 1004   |       1 |
		+----+-------------------------+-------------------------+------------+--------+---------+
		4 rows in set (0.00 sec)

		mysql>

	*/
}

6.一对多Association关联查询【不推荐】

package main

import (
	"encoding/json"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 2.建立一对多关系
type User struct {
	gorm.Model
	Username string `json:"username gorm:"column:username"`
	// 添加外键关联,只要该字段是一个CreditCard的切片,会自动和CreditCard模型建立外键关联。
	CreditCards []CreditCard
}

type CreditCard struct {
	gorm.Model
	Number string
	// 这个就是User表关联的外键,表示基于User表的ID主键(primary key)字段进行关联。
	UserID uint
}

func main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	fmt.Println(db)

	/**
	3.查询关联表数据

	使用Association方法:
		1)需要把User查询好;
		2)然后根据User定义中指定的AssociationForeignKey去查找CreditCard;

	这种查询方式需要使用2次回表查询,效率会很低,慎用!!!

	官方链接:
		https://gorm.io/zh_CN/docs/associations.html#%E6%9F%A5%E6%89%BE%E5%85%B3%E8%81%94
	*/

	// 3.1 先查询User表数据
	u := User{Username: "Jason Yin"}
	db.First(&u)
	//fmt.Printf("%v\n", u.Username)

	// 3.2 基于AssociationForeignKey外键字段"CreditCards"进行查找
	err := db.Model(&u).Association("CreditCards").Find(&u.CreditCards)
	if err != nil {
		panic(fmt.Sprintf("err ---> %v\n", err))
	}

	// 将其转换为json格式显示
	bytes, _ := json.Marshal(&u)
	fmt.Println(string(bytes))
}

7.一对多Preload预加载关联查询【推荐】

package main

import (
	"encoding/json"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 2.建立一对多关系
type User struct {
	gorm.Model
	Username string `json:"username gorm:"column:username"`
	// 添加外键关联,只要该字段是一个CreditCard的切片,会自动和CreditCard模型建立外键关联。
	CreditCards []CreditCard
}

type CreditCard struct {
	gorm.Model
	Number string
	// 这个就是User表关联的外键,表示基于User表的ID主键(primary key)字段进行关联。
	UserID uint
}

func main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	fmt.Println(db)

	/**
	3.使用Preload预加载方法:
		在查询User时先去获取CreditCard的记录

	很明显,这种效率会更高,一次性把数据查询出来,不会去回表查询两次。

	推荐阅读:
		https://gorm.io/zh_CN/docs/preload.html
	*/
	users := []User{}
	db.Preload("CreditCards").Find(&users)

	bytesUser, _ := json.Marshal(&users)
	fmt.Println(string(bytesUser))
}

四.多对多关联查询

1.对多对关系描述

Many to Many会在两个model中添加一张连接表。

举个例子:
	一个学生可以选择多个课程,一个课程又包含多个学生,这是双向对对多的关系。
	
推荐阅读:
	https://gorm.io/zh_CN/docs/many_to_many.html

2.多对多表结构创建

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 2.定义多对多关联表
type User struct {
	gorm.Model
	/**
	gorm:"many2many:user_languages;"
		指定了User表和Language表是多对多的关系,且"user_languages"是第三张连接表。
	*/
	Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
	gorm.Model
	Name string
}

func main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	/**
	3.创建多对多表结构

	创建成功后记得去数据库验证多对多表结构创建信息:
	mysql> SHOW TABLES;
	+-----------------------+
	| Tables_in_yinzhengjie |
	+-----------------------+
	| languages             |
	| user_languages        |
	| users                 |
	+-----------------------+
	3 rows in set (0.00 sec)

	mysql>
	mysql> DESC users;
	+------------+-----------------+------+-----+---------+----------------+
	| Field      | Type            | Null | Key | Default | Extra          |
	+------------+-----------------+------+-----+---------+----------------+
	| id         | bigint unsigned | NO   | PRI | NULL    | auto_increment |
	| created_at | datetime(3)     | YES  |     | NULL    |                |
	| updated_at | datetime(3)     | YES  |     | NULL    |                |
	| deleted_at | datetime(3)     | YES  | MUL | NULL    |                |
	+------------+-----------------+------+-----+---------+----------------+
	4 rows in set (0.01 sec)

	mysql>
	mysql> DESC languages;
	+------------+-----------------+------+-----+---------+----------------+
	| Field      | Type            | Null | Key | Default | Extra          |
	+------------+-----------------+------+-----+---------+----------------+
	| id         | bigint unsigned | NO   | PRI | NULL    | auto_increment |
	| created_at | datetime(3)     | YES  |     | NULL    |                |
	| updated_at | datetime(3)     | YES  |     | NULL    |                |
	| deleted_at | datetime(3)     | YES  | MUL | NULL    |                |
	| name       | longtext        | YES  |     | NULL    |                |
	+------------+-----------------+------+-----+---------+----------------+
	5 rows in set (0.00 sec)

	mysql>
	mysql> DESC user_languages;
	+-------------+-----------------+------+-----+---------+-------+
	| Field       | Type            | Null | Key | Default | Extra |
	+-------------+-----------------+------+-----+---------+-------+
	| user_id     | bigint unsigned | NO   | PRI | NULL    |       |
	| language_id | bigint unsigned | NO   | PRI | NULL    |       |
	+-------------+-----------------+------+-----+---------+-------+
	2 rows in set (0.00 sec)

	mysql>
	*/
	db.AutoMigrate(User{}, Language{})
}

3.多对多表结构自定义连接表

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

// 2.定义多对多关联表
type Person struct {
	ID   int
	Name string
	// 声明对对多关系,让Person和Address表进行关联
	Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
	ID   uint
	Name string
}

// 自定义第三张表时我们除了可以关联两个表的关联字段外,还可以添加一些我们需要的字段,比如"CreatedAt",”DeletedAt“等。
type PersonAddresses struct {
	// 对应Person表的主见
	PersonID int `gorm:"primaryKey"`
	// 对应Address表的主见
	AddressID int `gorm:"primaryKey"`
	CreatedAt time.Time
	DeletedAt gorm.DeletedAt
}

func main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	// 3.创建第三张表
	db.AutoMigrate(Person{}, Address{}, PersonAddresses{})

	/**
	4.添加数据

	进入数据库终端查询结构如下:
	mysql> SHOW TABLES;
	+-----------------------+
	| Tables_in_yinzhengjie |
	+-----------------------+
	| addresses             |
	| people                |
	| person_addresses      |
	+-----------------------+
	3 rows in set (0.00 sec)

	mysql> SELECT * FROM people;
	+----+-----------+
	| id | name      |
	+----+-----------+
	|  1 | Jason Yin |
	|  2 | 尹正杰    |
	+----+-----------+
	2 rows in set (0.00 sec)

	mysql> SELECT * FROM addresses;
	+----+-----------+
	| id | name      |
	+----+-----------+
	|  1 | 北京      |
	|  2 | 上海      |
	|  3 | 深圳      |
	|  4 | 陕西      |
	|  5 | 河北      |
	|  6 | 山东      |
	|  7 | 黑龙江    |
	+----+-----------+
	7 rows in set (0.00 sec)

	mysql> SELECT * FROM person_addresses;
	+-----------+------------+------------+------------+
	| person_id | address_id | created_at | deleted_at |
	+-----------+------------+------------+------------+
	|         1 |          1 | NULL       | NULL       |
	|         1 |          2 | NULL       | NULL       |
	|         1 |          3 | NULL       | NULL       |
	|         2 |          4 | NULL       | NULL       |
	|         2 |          5 | NULL       | NULL       |
	|         2 |          6 | NULL       | NULL       |
	|         2 |          7 | NULL       | NULL       |
	+-----------+------------+------------+------------+
	7 rows in set (0.00 sec)

	mysql>

	*/
	p1 := Person{
		ID:   1,
		Name: "Jason Yin",
		Addresses: []Address{
			{ID: 1, Name: "北京"},
			{ID: 2, Name: "上海"},
			{ID: 3, Name: "深圳"},
		},
	}

	p2 := Person{
		ID:   2,
		Name: "尹正杰",
		Addresses: []Address{
			{ID: 4, Name: "陕西"},
			{ID: 5, Name: "河北"},
			{ID: 6, Name: "山东"},
			{ID: 7, Name: "黑龙江"},
		},
	}
	db.Create(&p1)
	db.Create(&p2)

}

4.多对多Preload预加载

package main

import (
	"encoding/json"
	"fmt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

// 定义多对多关系
type Person struct {
	ID        int
	Name      string
	Addresses []Address `gorm:"many2many:person_addresses;"`
}
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 main() {
	// 1.创建gorm的链接
	dsn := "admin:yinzhengjie@tcp(10.0.0.20:3306)/yinzhengjie?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

	// 1.获取所有用户的地址
	persons := []Person{}
	db.Preload("Addresses").Find(&persons)
	allData, _ := json.Marshal(&persons)
	fmt.Println(string(allData))

	// 2.获取 name="Jason Yin" 用户的地址
	person := Person{Name: "Jason Yin"}
	db.Preload("Addresses").Find(&person)
	oneData, _ := json.Marshal(&person)
	fmt.Println(string(oneData))

}

posted @ 2023-11-22 02:57  尹正杰  阅读(199)  评论(0编辑  收藏  举报