[GO] GORM入门使用

news2024/12/25 23:38:06

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75904.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【cer转jks】

需要两个文件.key,和.cer https://www.myssl.cn/tools/merge-jks-cert.html 选择JKS在线生成 请参照示例&#xff0c;将密钥文件(KEY文件)复制到输入框 请参照示例&#xff0c;将证书文件(CRT/CER文件)复制到输入框 输入别名和密码即可生成 JAVA的application.properties中填写…

主流编程语言的底层实现是什么以及gcc,clang,llvm等编译器的区别

文章目录一、前言二、c和c和c#的区别1、高级语言和低级语言2、c 和 c 和 c#的区别&#xff08;1&#xff09;C语言&#xff08;2&#xff09;C三、各主流语言的底层实现1、python的底层实现2、 java的底层实现3、php的底层实现4、js的底层实现5、node是用什么语言写的6、golang…

[激光原理与应用-45]:《焊接质量检测》-2- 常见焊接缺陷与检验方法

目录 一、概述 二、焊接缺陷的分类 2.1 按产生原因 2.2 按性质分有&#xff1a; 2.3 按在焊缝中的位置分有&#xff1a; 三、焊接缺陷检验的常用方法 一、概述 对于一个金属结构来说&#xff0c;焊接检验就是对所有焊缝或焊接接头而言的&#xff0c;也就是对焊接缺陷的检…

2022年高压快充行业研究报告

第一章 行业概况 高压快充即为快速充电&#xff0c;衡量单位可用充电倍率&#xff08;C&#xff09;表示。充电倍率越大&#xff0c;充电时间越短。依据公式&#xff0c;电池充电的倍率&#xff08;C&#xff09;充电电流&#xff08;mA&#xff09;/电池额定容量&#xff08;…

Python爬虫——Scrapy框架

Scrapy是用python实现的一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。使用Twisted高效异步网络框架来处理网络通信。 Scrapy架构&#xff1a; ScrapyEngine&#xff1a;引擎。负责控制数据流在系统中所有组件中流动&#xff0c;并在相应动作发生时触发事…

西部广播电视杂志《西部广播电视》杂志社《西部广播电视》编辑部2022年第21期目录

特稿&#xff1a;乡村振兴战略下的媒体实践《西部广播电视》投稿&#xff1a;cnqikantg126.com 乡村振兴中广播电视角色定位研究 王菾; 1-4 对农宣传中广播电视传播功能研究 周艺培; 5-711 广播电视助力涉农产业发展 胡朗铭; 8-11 省级乡村频道发展的典型个案研…

答疑1209

1、在fmu v2中回传电压值 在comm task 里面有电压读取的任务&#xff0c;pool 电池的电压状态 这是上层会调用hal层&#xff0c;也就是adc.c里面的read函数 主要是fmu v2 里面没有写adc的驱动函数&#xff0c;也就是driver层&#xff0c;这里需要模仿v5上面的驱动来补充一下&a…

[附源码]计算机毕业设计交通事故档案管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Pytest框架批量安装插件解析

1、新建一个工程 使用新的环境变量 1.1.插件文件 新建一个txt的文件&#xff0c;将常用插件放在该文件中&#xff0c;如下图 文件名&#xff1a;requirements.txt 常用插件&#xff1a; pytest pytest-html pytest-xdist pytest-ordering pytest-rerunfailures allure-pyt…

探究菊花文的“密码”(文中转换器源码仅一行代码哦)

菊花文、方框文&#xff0c;看起来很神奇的样子。其实&#xff0c;也就是在字符串中插入了特殊字符&#xff0c;利用特殊字符的显示特性获得的混合显示效果而已。(文中转换器源码仅一行代码哦&#x1f917;) (本文获得CSDN质量评分【90】)【学习的细节是欢悦的历程】Python 官网…

Alpine安装Oracle JDK存在的问题

简介 前面我们提到了Alpine使用的不是正统的glibc&#xff0c;对于一些强依赖glibc的系统建议不要使用Alpine&#xff0c;比如使用了Oracle JDK的系统&#xff0c;建议在Alpine换成OpenJDK。 Alpine官方给出了Alpine的三大特征 Small、Simple、Secure&#xff0c;但其实我们知…

【论文阅读】 AdaptivePose: Human Parts as Adaptive Points

DOI&#xff1a;https://doi.org/10.1609/aaai.v36i3.20185 AAAI 2022 Published&#xff1a;2022-06-28 Others阅读/整理&#xff1a;翻译1、翻译2 Intro&Background 多人姿态估计方法 two-stage methods【图a】 这些方法使用绝对关键点位置&#xff0c;定位的…

顺时针打印矩阵

大概题意为&#xff1a; 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步 &#xff1a; 这样核心就设计好了&#xff0c;接下来设计剩余的东西 设计题目程序 1.题目要求我们返回一个地址&#xff0c;所以我们创造一个一维数组&#xff0c;它的元素个数为行…

华为机试_HJ61 放苹果【简单】【收藏】

目录 描述 输入描述&#xff1a; 输出描述&#xff1a; 解题过程 提交代码 递归方法 动态规划方法 学习代码 递归方法 动态规划方法 收藏点 描述 把m个同样的苹果放在n个同样的盘子里&#xff0c;允许有的盘子空着不放&#xff0c;问共有多少种不同的分法&#xff…

Postman(七): postman应用实战

Postman(13): postman应用实战 postman应用实战 下面以微信公众平台举例&#xff1a; 第一步、先创建文件夹 第二步、打开postman&#xff0c;创建collections 第三步、设置环境变量&#xff0c;全局变量 设置环境变量&#xff1b;如下图&#xff1a; 设置全局变量&#xf…

银河麒麟V10 + 飞腾CPU编译安装 Qt5.9.9

一、准备工作 1. 下载Qt源码包 这里我们要编译的是Qt5.9.9&#xff0c;下载网址&#xff1a;https://download.qt.io/archive/qt/5.9/5.9.9/single/ 在任意空闲位置新建文件夹&#xff0c;并将源码包放到该目录下。&#xff08;这里在/home目录下新建名为Qt_Source的文件夹&a…

Unity Animancer插件(二)精确控制

一、通过名称播放动画 前面我们讲的都是直接通过动画片段的引用播放动画&#xff0c;Animancer也提供了直接通过动画名称来播放动画的方法。但这并不是推荐的使用方式&#xff0c;因为通过字符串播放比通过引用播放效率略低&#xff0c;且更难维护。 首先我们需要在角色身上挂…

2022年ACM杰出会员名单公布:23位华人学者入选

12月7日&#xff0c;2022年度ACM杰出会员&#xff08;Distinguished Member&#xff09;名单公布&#xff01; 本次评选设有三个奖项&#xff0c;分别表彰在计算机领域做出的教育贡献、工程贡献和科学贡献。 ACM创立于1947年&#xff0c;目前在全球130多个国家和地区拥有超过…

OceanBase 4.0 解读:降低分布式数据库使用门槛,谈谈我们对小型化的思考

关于作者 赵裕众 OceanBase 资深技术专家&#xff0c;2010 年加入支付宝后从事分布式事务框架的研发&#xff0c;2013 年加入 OceanBase 团队&#xff0c;目前负责存储引擎相关的研发工作。 近年来&#xff0c;随着应用场景多样化和数据量的增长&#xff0c;我们看到分布式数据…

判别分析-书后习题回顾总结

5-2 题目 理论基础 多总体的距离判别 马氏距离&#xff1a;dG2(x)(x−μ)2σ2d^{2}_{G}(x)\frac{(x-\mu)^{2}}{\sigma^{2}}dG2​(x)σ2(x−μ)2​ 取马氏距离最小的那一个&#xff0c;就属于这类。 贝叶斯判别准则 计算qtft(x)q_{t}\times f_{t}(x)qt​ft​(x) ft(x)12πσ…