[GO] GORM入门使用

1. GORM

1.1 什么是ORM

  1. ORM是object relational mapping就是对象映射关系程序
  2. 简单的说类似python这种面向对象的程序来说,一切都是对象.
  3. 为了保证一致性的使用习惯,通过orm将编程语言的对象模型和数据库的关系型模型建立映射关系
  4. 这样我们直接使用编程语言的对象模型进行数据库操作就可以了.而不是直接使用sql语言.

1.2 什么是GORM

GORM是Golang ORM库

  • 全功能 ORM
  • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
  • Create,Save,Update,Delete,Find 中钩子方法
  • 支持 PreloadJoins 的预加载
  • 事务,嵌套事务,Save Point,Rollback To Saved Point
  • Context、预编译模式、DryRun 模式
  • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
  • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
  • 复合主键,索引,约束
  • Auto Migration
  • 自定义 Logger
  • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

1.3 初始化项目

mkdir day04
cd day04
go mod init day04

1.4 安装GORM

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

2. 创建数据库

docker run --name mysql -itd -h mysql-server -e MYSQL_ROOT_PASSWORD=root -v /data/mysql5.7:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456  mysql:5.7.34 --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
root@Ubuntu-1:/data# docker exec -it 0af13655 bash
root@mysql-server:/# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 5
Server version: 5.7.34 MySQL Community Server (GPL)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database test_db charset utf8;
Query OK, 1 row affected (0.00 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test_db            |
+--------------------+
5 rows in set (0.00 sec)

3. 连接数据库

先要确认数据库连接正常.

package main

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

func main() {
    
    
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	if err != nil {
    
    
		panic(err)	// 异常处理
	}
	fmt.Println("db:", db)
    结果
    db: &{
    
    0xc0001245a0 <nil> 0 0xc0001d2380 1}

err结果是nil就说明连接正常.

如果失败会报以下错误

[error] failed to initialize database, got error Error 1045 (28000): Access denied for user 'root'@'192.168.31.1' (using password:
YES)

4. 创建表

// 1. 定义字段
type User struct {
    
    
	// gorm:"primary_key" 主键
	Id       int64  `json:"id" gorm:"primary_key"`
	Username string `json:"username" gorm:"username"`
	Password string `json:"password" gorm:"password"`
}

func main() {
    
    
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	if err != nil {
    
    
		panic(err)
	}
	// 2. 创建表
	// 迁移 https://gorm.io/zh_CN/docs/migration.html
	db.AutoMigrate(User{
    
    })
}
mysql> desc users;
+----------+------------+------+-----+---------+----------------+
| Field    | Type       | Null | Key | Default | Extra          |
+----------+------------+------+-----+---------+----------------+
| id       | bigint(20) | NO   | PRI | NULL    | auto_increment |
| username | longtext   | YES  |     | NULL    |                |
| password | longtext   | YES  |     | NULL    |                |
+----------+------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

运行后该表就被创建了

请添加图片描述

5. 增删改查

5.1 增

	db.Create(&User{
    
    
		Username: "zhangsan",
		Password: "123456",
	})
mysql> select * from users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | zhangsan | 123456   |
+----+----------+----------+
1 row in set (0.00 sec)

5.2 改

	// 5. 修改字段
	db.Model(User{
    
    
		Id: 1,
	}).Update("password", "10JQKA")
mysql> select * from users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | zhangsan | 10JQKA   |
+----+----------+----------+
1 row in set (0.00 sec)

5.3 查询

再增加一条数据

mysql> select * from users;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | zhangsan | 10JQKA   |
|  2 | lisi     | 111111   |
+----+----------+----------+
2 rows in set (0.00 sec)

查询一条数据

	// 查询
	// 过滤单挑数据
	u := User{
    
    Id: 2}
	db.First(&u)
	fmt.Printf("%#v\n", u)
	// 查询所有数据
	users := []User{
    
    }
	db.Find(&users)
	fmt.Printf("%#v\n", users)
结果
main.User{
    
    Id:2, Username:"lisi", Password:"111111"}
[]main.User{
    
    main.User{
    
    Id:1, Username:"zhangsan", Password:"10JQKA"}, main.User{
    
    Id:2, Username:"lisi", Password:"111111"}}

5.4 删除

	// 删除数据
	db.Delete(&User{
    
    Id: 1})
	db.Find(&users)
	fmt.Printf("%#v\n", users)
	// 条件删除
	db.Where("Username = ?", "lisi").Delete(&User{
    
    })
	db.Find(&users)
	fmt.Printf("%#v\n", users)
结果
[]main.User{
    
    main.User{
    
    Id:2, Username:"lisi", Password:"111111"}}
[]main.User{
    
    }

6. 模型类型

6.1 数据类型

参数 含义
paimary_key 主键
string 默认字符串对应的是text文本类型
*time.Time datatime
varchar(100) 将默认string设置成varchar(100)
unique_index 唯一索引
size:255 设置字段大小255个字节
not null 不为空
unique 字段唯一
AUTO_INCREMENT 自增
index:addr 创建一个名字是addr的索引
gorm:“-” 忽略这个字段

先创建表

type User struct {
    
    
	Id           int64 `gorm:"paimary_key" json:"id"`
	Name         string
	CreateAt     *time.Time `json:"createAt" gorm:"column:create_at"`
	Email        string     `gorm:"type:varchar(100);unique_index"`
	Role         string     `gorm:"size:255"`
	MemberNumber int        `gorm:"unique;not null"`
	Num          int        `gorm:"AUTO_INCREMENT"`
	Address      string     `gorm:"index:addr"`
	IgnoreMe     int        `gorm:"-"`
}

func main() {
    
    
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	db.AutoMigrate(User{
    
    })
}
mysql> desc users;
+---------------+--------------+------+-----+---------+----------------+
| Field         | Type         | Null | Key | Default | Extra          |
+---------------+--------------+------+-----+---------+----------------+
| id            | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| name          | longtext     | YES  |     | NULL    |                |
| create_at     | datetime(3)  | YES  |     | NULL    |                |
| email         | varchar(100) | YES  |     | NULL    |                |
| role          | varchar(255) | YES  |     | NULL    |                |
| member_number | bigint(20)   | NO   | UNI | NULL    |                |
| num           | bigint(20)   | YES  |     | NULL    |                |
| address       | varchar(191) | YES  | MUL | NULL    |                |
+---------------+--------------+------+-----+---------+----------------+
8 rows in set (0.00 sec)

6.2 支持结构标签

标签名 说明
column 指定 db 列名
type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not nullsize, 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 迁移时为字段添加注释

7. 一对多关联查询

请添加图片描述

通过外键实现两张表之间的关联.

type User struct {
    
    
	gorm.Model
	CreditCards []CreditCard
}

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

7.1 外键

  • 为了定义一对多关系,外键必须存在,默认外键的名字是所有者类型的名字加上他的主键.
  • 就像上面的例子,为了定义一个属于User的模型,外键就应该为UserID
  • 使用其他的字段名字作为外键,可以通过foreignkey来定制它
type User struct {
    
    
	gorm.Model
    CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}

7.1.1 建立一对多表

// 2. 创建一对多
// 2.1 定义User表
type User struct {
    
    
	gorm.Model
	Username string `json:"username" gorm:"column:username"`
	// 添加外键关联
	CreditCard []Card
}

// 2.2 定义Card表
type Card struct {
    
    
	gorm.Model
	Number string
	// 这个就是User表关联的外键 名字是 结构体+主键
	UserID uint
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	fmt.Println(db)
	// 3. 创建表结构
	db.AutoMigrate(&User{
    
    }, &Card{
    
    })
	// 4. 添加一对多
}

表结构

mysql> desc cards;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) 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    |                |
| number     | longtext            | YES  |     | NULL    |                |
| user_id    | bigint(20) unsigned | YES  | MUL | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)

mysql> desc users;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) 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    |                |
| username   | longtext            | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

7.1.2 创建一对多数据

// 2. 创建一对多
// 2.1 定义User表
type User struct {
    
    
	gorm.Model
	Username string `json:"username" gorm:"column:username"`
	// 添加外键关联
	CreditCard []Card
}

// 2.2 定义Card表
type Card struct {
    
    
	gorm.Model
	Number string
	// 这个就是User表关联的外键 名字是 结构体+主键
	UserID uint
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	fmt.Println(db)
	// 3. 创建表结构
	//db.AutoMigrate(&User{}, &Card{})

	// 4. 添加一对多
	user := User{
    
    
		Username: "zhangsan",
		CreditCard: []Card{
    
    
			{
    
    Number: "00001"},
			{
    
    Number: "00002"},
		},
	}
	db.Create(&user)
	fmt.Println(db)
}

此时数据已经被创建

mysql> select * from users;
+----+-------------------------+-------------------------+------------+----------+
| id | created_at              | updated_at              | deleted_at | username |
+----+-------------------------+-------------------------+------------+----------+
|  1 | 2022-12-07 10:55:11.397 | 2022-12-07 10:55:11.397 | NULL       | zhangsan |
+----+-------------------------+-------------------------+------------+----------+
1 row in set (0.00 sec)

mysql> select * from cards;
+----+-------------------------+-------------------------+------------+--------+---------+
| id | created_at              | updated_at              | deleted_at | number | user_id |
+----+-------------------------+-------------------------+------------+--------+---------+
|  1 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL       | 00001  |       1 |
|  2 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL       | 00002  |       1 |
+----+-------------------------+-------------------------+------------+--------+---------+
2 rows in set (0.00 sec)

7.1.3 添加一对多数据

	// 给zhangsan添加一个信用卡
	// 查出zhangsan相关的记录
	u := User{
    
    Username: "zhangsan"}
	db.First(&u)
	// Association 关联查找
	db.Model(&u).Association("CreditCard").Append([]Card{
    
    
		{
    
    Number: "00003"},
	})

此时就在Card表中追加了一条00003的记录

mysql> select * from users;
+----+-------------------------+-------------------------+------------+----------+
| id | created_at              | updated_at              | deleted_at | username |
+----+-------------------------+-------------------------+------------+----------+
|  1 | 2022-12-07 10:55:11.397 | 2022-12-07 11:33:36.351 | NULL       | zhangsan |
+----+-------------------------+-------------------------+------------+----------+
1 row in set (0.00 sec)

mysql> select * from cards;
+----+-------------------------+-------------------------+------------+--------+---------+
| id | created_at              | updated_at              | deleted_at | number | user_id |
+----+-------------------------+-------------------------+------------+--------+---------+
|  1 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL       | 00001  |       1 |
|  2 | 2022-12-07 10:55:11.415 | 2022-12-07 10:55:11.415 | NULL       | 00002  |       1 |
|  3 | 2022-12-07 11:33:36.352 | 2022-12-07 11:33:36.352 | NULL       | 00003  |       1 |
+----+-------------------------+-------------------------+------------+--------+---------+
3 rows in set (0.00 sec)

7.2 一对多查找

7.2.1 Association

使用Association方法要把User查询好,然后根据User定义中指定的AssociationForeignKey去查找Card,性能较低

// 2. 创建一对多
// 2.1 定义User表
type User struct {
    
    
	gorm.Model
	Username string `json:"username" gorm:"column:username"`
	// 添加外键关联
	CreditCard []Card
}

// 2.2 定义Card表
type Card struct {
    
    
	gorm.Model
	Number string
	// 这个就是User表关联的外键 名字是 结构体+主键
	UserID uint
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	// 3. 查询关联表数据
	// 3.1 使用Association 方法把User查询好
	// 3.2然后根据User中定义的AssociationForeignKey去查找Card
	u := User{
    
    Username: "zhangsan"}
	db.First(&u)
	//fmt.Printf("%v", u.Username)
	err := db.Model(&u).Association("CreditCard").Find(&u.CreditCard)
	if err != nil {
    
    
		fmt.Println("err", err)
	}
	fmt.Println(u)
	// 转成json数据
	strUser, err := json.Marshal(&u)
	if err != nil {
    
    
		fmt.Println("err", err)
	}
	fmt.Println(string(strUser))
结果
{
    
    "ID":1,"CreatedAt":"2022-12-07T10:55:11.397+08:00","UpdatedAt":"2022-12-07T11:33:36.351+08:00","DeletedAt":null,"username":"zhangs
an","CreditCard":[{"ID":1,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","DeletedAt":null,
"Number":"00001","UserID":1},{
    
    "ID":2,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","Delet
edAt":null,"Number":"00002","UserID":1},{"ID":3,"CreatedAt":"2022-12-07T11:33:36.352+08:00","UpdatedAt":"2022-12-07T11:33:36.352+08
:00","DeletedAt":null,"Number":"00003","UserID":1}]}

7.2.2 Preload预加载

// 2. 创建一对多
// 2.1 定义User表
type User struct {
    
    
	gorm.Model
	Username string `json:"username" gorm:"column:username"`
	// 添加外键关联
	CreditCard []Card
}

// 2.2 定义Card表
type Card struct {
    
    
	gorm.Model
	Number string
	// 这个就是User表关联的外键 名字是 结构体+主键
	UserID uint
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	//fmt.Println(db)
	// 3. 通过Preload进行一对多的查询
	users := []User{
    
    }
	db.Preload("CreditCard").Find(&users)
	// 4. 通过FindOne查询
	strUser, _ := json.Marshal(&users)
	fmt.Println(string(strUser))
}
结果
[{
    
    "ID":1,"CreatedAt":"2022-12-07T10:55:11.397+08:00","UpdatedAt":"2022-12-07T11:33:36.351+08:00","DeletedAt":null,"username":"zhang
san","CreditCard":[{"ID":1,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","DeletedAt":null
,"Number":"00001","UserID":1},{
    
    "ID":2,"CreatedAt":"2022-12-07T10:55:11.415+08:00","UpdatedAt":"2022-12-07T10:55:11.415+08:00","Dele
tedAt":null,"Number":"00002","UserID":1},{"ID":3,"CreatedAt":"2022-12-07T11:33:36.352+08:00","UpdatedAt":"2022-12-07T11:33:36.352+0
8:00","DeletedAt":null,"Number":"00003","UserID":1}]}]

8. 多对多关系

双向的一对多,就是多对多
Many to Many 会在两个 model 中添加一张连接表。
请添加图片描述

8.1 many2many

type User struct {
    
    
	gorm.Model
	// Languages 是字段名
	// gorm:"many2many指定了User表和Language是多对多关系 user_language是第三张表的表名
	Languages []Language `gorm:"many2many:user_language"`
}

type Language struct {
    
    
	gorm.Model
	Name string
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	fmt.Println(db)
	// 3. 创建表结构
	db.AutoMigrate(User{
    
    }, Language{
    
    })
}

执行后User和Language被创建,同时第三张表user_language也被创建

mysql> desc users;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) 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.00 sec)

mysql> desc languages;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) 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> desc user_language;
+-------------+---------------------+------+-----+---------+-------+
| Field       | Type                | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+-------+
| user_id     | bigint(20) unsigned | NO   | PRI | NULL    |       |
| language_id | bigint(20) unsigned | NO   | PRI | NULL    |       |
+-------------+---------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

8.2 自定义第三张表

// 2. 定义多对多
// 一个用户可以多个地址,一个地址可以对应多个用户
type Persons struct {
    
    
	Id   string
	Name string `gorm:"many2many:PersonAddress"`
}
type Addresses struct {
    
    
	Id   uint
	Name string
}

// 3. 定义第三张表
type PersonAddresses struct {
    
    
	PersonId  string `gorm:"primaryKey"` // 对应表的主键
	AddressId string `gorm:"primaryKey"` // 对应表的主键
	CreateAt  time.Time
}

func main() {
    
    
	// 1. 建立连接
	// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
	dsn := "root:123456@tcp(192.168.31.24:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
	db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{
    
    })
	fmt.Println(db)
	db.AutoMigrate(Persons{
    
    }, Addresses{
    
    }, PersonAddresses{
    
    })
}

创建后

mysql> show tables;
+-------------------+
| Tables_in_test_db |
+-------------------+
| addresses         |
| languages         |
| person_addresses  |
| persons           |
| user_language     |
| users             |
+-------------------+
6 rows in set (0.00 sec)
mysql> desc addresses;
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| name  | longtext            | YES  |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> desc persons;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | varchar(191) | NO   | PRI | NULL    |       |
| name  | longtext     | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> desc person_addresses;
+------------+--------------+------+-----+---------+-------+
| Field      | Type         | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| person_id  | varchar(191) | NO   | PRI | NULL    |       |
| address_id | varchar(191) | NO   | PRI | NULL    |       |
| create_at  | datetime(3)  | YES  |     | NULL    |       |
+------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

9. 中间件

9.1 中间件

  • Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数.
  • 这个钩子函数就是中间件,中间件适合处理一些公共的业务逻辑

9.2 全局中间件

// 定义一个全局中间件(所有路由都会使用)
// MiddleWare 函数名字,随便写.
// gin.HandlerFunc中间件必须要返回的方法
func MiddleWare() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		fmt.Println("我是一个全局中间件")
	}
}

func main() {
    
    
	// 1. 实例化引擎
	r := gin.Default()
	// 5. 全局使用中间件
	r.Use(MiddleWare())
	// 2. 定义路由
	r.GET("/", func(c *gin.Context) {
    
    
		fmt.Println("访问了/")
		c.JSON(200, gin.H{
    
    
			"code": 200,
			"msg":  "Success",
		})
	})
	fmt.Println("http://192.168.31.1:8000")
	r.Run(":8000")

}

9.3 局部中间件

只对指定的路劲进行限制

// 定义一个全局中间件(所有路由都会使用)
// MiddleWare 函数名字,随便写.
// gin.HandlerFunc中间件必须要返回的方法
func MiddleWare() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		fmt.Println("我是一个全局中间件")
	}
}

func main() {
    
    
	// 1. 实例化引擎
	r := gin.Default()
	// 5. 全局使用中间件
	//r.Use(MiddleWare())
	// 2. 定义路由
	r.GET("/", func(c *gin.Context) {
    
    
		fmt.Println("访问了/")
		c.JSON(200, gin.H{
    
    
			"code": 200,
			"msg":  "Success",
		})
	})
	r.GET("/home", MiddleWare(), func(c *gin.Context) {
    
    
		fmt.Println("访问了/home")
		c.JSON(200, gin.H{
    
    
			"code": 200,
			"msg":  "In Home",
		})
	})
	fmt.Println("http://192.168.31.1:8000")
	r.Run(":8000")
}
此时访问/不会再有加载中间件
但访问/home就会加载
访问了/
[GIN] 2022/12/07 - 15:26:20 | 200 |            0s |    192.168.31.1 | GET      "/"
我是一个全局中间件
访问了/home
[GIN] 2022/12/07 - 15:26:26 | 200 |       608.7µs |    192.168.31.1 | GET      "/home"

9.4 Next方法

  1. 先执行中间件.
  2. 执行c.next(),那么就交给了视图函数执行
  3. 视图函数执行完毕后,返回中间件继续往下执行

整个过程中MiddleWare只被调用了1次,处理完视图函数后继续执行后续操作.

// 定义一个全局中间件(所有路由都会使用)
// MiddleWare 函数名字,随便写.
// gin.HandlerFunc中间件必须要返回的方法
func MiddleWare() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		fmt.Println("开始执行中间件")
		c.Next()
		fmt.Println("视图函数执行完成后再调用next()方法")
	}
}

func main() {
    
    
	// 1. 实例化引擎
	r := gin.Default()
	// 5. 全局使用中间件
	r.Use(MiddleWare())
	// 2. 定义路由
	r.GET("/", func(c *gin.Context) {
    
    
		fmt.Println("访问了/")
		c.JSON(200, gin.H{
    
    
			"code": 200,
			"msg":  "Success",
		})
	})
	fmt.Println("http://192.168.31.1:8000")
	r.Run(":8000")

}
结果
开始执行中间件
访问了/
视图函数执行完成后再调用next()方法
[GIN] 2022/12/07 - 15:31:53 | 200 |            0s |    192.168.31.1 | GET      "/"

9.5 实现Token认证

/index页面无需登录即可访问 http://192.168.31.1:8000/index
/home目录需要登录验证成功才能访问 http://192.168.31.1:8000/home

func Auth() gin.HandlerFunc {
    
    
	return func(c *gin.Context) {
    
    
		Token := c.Request.Header.Get("token")
		fmt.Println("获取到的Token为: ", Token)
		if Token != "twgdh" {
    
    
			c.String(403, "身份验证失败,请先登录!")
			c.Abort() // 终止当前请求,不再转发给路由,处理函数不再执行
			return
		}
		c.Next()
	}
}

func main() {
    
    
	r := gin.Default()
	// 无需登录就能访问
	r.GET("/index", func(c *gin.Context) {
    
    
		c.JSON(200, gin.H{
    
    "msg": "Index Page!无需登录就能访问"})
	})
	// 需要登录才能访问
	r.GET("/home", Auth(), func(c *gin.Context) {
    
    
		c.JSON(200, gin.H{
    
    "msg": "Home Page!您已登录成功"})
	})
	fmt.Println("http://192.168.31.1:3000/index")
	fmt.Println("http://192.168.31.1:3000/home")
	r.Run(":3000")
}

/index 无需token

请添加图片描述

/home 当token不等于twgdh时,提示登录失败

请添加图片描述

/home当获取到正确的token时,提示登录成功

请添加图片描述

10. Restful 风格

10.1 什么是RESTful

  • Restful是一种软件架构风格(REST是Representational State Transfer的简称,中文意思为: 表征状态转移)
  • REST从资源角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识
  • 所有数据不过是通过网络获取还是操作(增删改查)的数据,都是资源,将一切数据视为资源就是REST区别于其他架构风格的最本质熟悉

10.2 web开发本质

  • 对数据库的增删改查操作
  • Restful风格就是把所有数据都当做资源,对表的操作就是对资源的操作
  • 在url通过资源名称来指定资源
  • 通过get/post/put/delete/patch 对资源进行操作
    • get 获取数据信息(一条或多条)
    • post 添加数据
    • put 修改数据
    • delete 删除数据

10.2.1 URL路径

路径都使用名词或名词的复数,不要使用动词

获取所有信息 GET /products
获取1条信息 GET /products/4
新增信息 POST /products
更新一条信息 PUT /products/4

10.2.2 请求方式

访问一个URL地址,采用不同的求情方式,代表要执行不同的操作

请求方式 说明
GET 获取一个或多个资源
POST 新增资源数据
PUT 修改资源数据
DELETE 删除资源数据

10.2.3 过滤信息

通过?进行数据过滤

?limit=10 指定返回记录的数量
?offset=10 指定返回记录开始位置
?page=2&pagesize=100 指定第几页开始,每页的记录数
?sortby=name&order=asc 指定返回结果按照哪个熟悉排序,以及排序的顺序

10.2.4 响应状态码

状态码 含义
2xx 都是请求成功的
200 请求成功,一般用于GET和POST请求
201 Created [POST/PUT/PATCH] 用户新建或修改数据成功
202 Accept [*] 表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT [DELETE],用户删除数据成功
3xx 重定向
301 永久重定向
302 临时重定向
4xx 客户端错误
400 INVALID REQUEST [POST/PUT/PATCH] 用户发出的请求有错误.
401 Unauthorized [*] 授权错误 用户没有权限(令牌,用户名,密码错误)
403 Forbidden [*] 授权正确,但操作被禁止. 表示用户得到授权(与401相同),但访问是被禁止的.
404 NOT FOUND [*] 用户发出的请求记录不存在
406 Not Acceptale [GET] 用户请求的格式不可得(比如需要json,提交了xml)
410 Gone [GET] 用户请求的资源被永久删除了,切不会再得到.
422 Unprocesable entity [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误
5xx 服务端错误
500 INTERNAL SERVER ERROR [*] 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求

猜你喜欢

转载自blog.csdn.net/qq_29974229/article/details/128254584