Go gorm库(详细版)

目录

01. 什么是ORM

02. 环境搭建

03. 连接数据库

高级设置

gorm 的命名策略

创建表

日志显示

04. 模型定义

定义一张表

自动生成表结构

修改表字段大小

字段标签

05. 单表查询

5.1 表结构

5.2 添加单条记录

5.3 批量插入

5.4 单条数据查询

5.5 根据主键查询

5.6 根据结构体进行查询

5.7 获取查询的结构

5.8 查询多条记录并返回Json数据

5.9 根据主键列表去查询

06. 更新数据

6.1 Save 保存所有字段

Select更新指定字段​编辑

6.2 Update批量更新

6.3 Updates更新

07. 删除数据

08. 添加钩子函数(HOOK)

09. Gorm高级查询

9.01 Where查询

9.02 Select 选择字段

9.03 排序

9.04 分页查询

9.05 去重

​编辑9.06 分组查询

9.07 gorm执行原生sql

9.08 子查询

9.09 命名参数

9.10 从Find到Map

9.11 查询引用Scope

10. 一对多关系

10.1 表结构建立

重写外键关联

10.2 添加数据

外键添加

10.3 查询数据

预加载

嵌套预加载

带条件的预加载

自定义预加载

10.4 删除数据

级联删除

清除外键关系

11. 一对一关系

表结构搭建

添加记录

查询

删除

12. 多对多关系

12.1 表结构搭建

12.2 添加

添加文章,并创建标签

创建文章,添加已有标签

12.3 查询

12.4 更新

12.5 多对多自定义连接表(第三张表)

12.5.1 表结构及生成

12.5.2 操作案例

1. 添加文章并添加标签,并自动关联

2. 添加文章,关联已有标签

3. 给已有文章关联标签

4. 替换已有文章的标签

5. 查询文章列表,显示标签

SetupJoinTable

12.5.3 自定义连接表主键

生成表结构

12.5.4 操作连接表(自定义连接表的时候)

13. 自定义数据类型

13.1 存储json

插入数据

查询数据

13.2 存储数组

13.3 枚举类型

枚举1.0

枚举2.0

枚举3.0(用这个)

在grom中使用

14. gorm 事务

14.1 普通事务

14.2 手动事务


01. 什么是ORM

02. 环境搭建

go mod init 文件名

go get gorm.io/driver/mysql //mysql的驱动

go get "gorm.io/gorm"

03. 连接数据库

package mainimport ("fmt""gorm.io/driver/mysql""gorm.io/gorm"
)// 定义一个全局变量db,用于后面数据库的读写操作,通常就放在全局里面
var DB *gorm.DBfunc init() {username := "root"       //账号password := "password"   //密码host := "IP"             //数据库地址port := "3306"           //端口Dnname := "dtbase"       //数据库名timeout := "10s"         //连接超时,10s//root:root@tcp(127.0.0.1:3306)/test?dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?timeout=%s", username, password, host, port, Dnname, timeout)//连接mysql,获得DB类型实例,用于后面数据库的读写操作db, err := gorm.Open(mysql.Open(dsn))if err != nil {panic("连接数据库失败,error=" + err.Error())}DB = db//连接成功fmt.Println("连接数据库成功")
}func main() {}

高级设置

是在open的地方设置

gorm 的命名策略

创建表

注意虽然我们的结构为Student,但是表名却是studens,并且字段全小写,至于为什么是这样,就是我们上面gorm命名规则的原因了

日志显示

gorm默认日志是只打印错误和慢sql,我们可以设置日志的显示等级

可以设置日志等级为info,但是这样显示会很占用空间

推荐:

04. 模型定义

模型是标准的 struct ,由Go的基本数据类型,实现了Scanner和Valuer接口的自定义类型及其指针或别名组成

定义一张表

PS:小写属性是不会生成字段的

自动生成表结构

AutoMigrate的逻辑是只新增,不擅长,不修改(大小会修改)

例如将 Name 修改为 Name1,进行迁移,会多出一个name1的字段

新增Email和改Name为Name1

修改表字段大小

有两种方式

下面那种样例

字段标签

05. 单表查询

5.1 表结构

5.2 添加单条记录

添加记录就是实例化结构体

有两条是因为点了两下哈

如果此时

那么他们分别是空字符串和null

这就是指针的好处,当然email也可以直接传nil因为他是指针

还可以打印s1看看

5.3 批量插入

Creat方法还可以用于插入多条记录

5.4 单条数据查询

会查询第一个数据

Take就是传统的:SELECT * FROM 'students' LIMIT 1

而Frist和Last是按照主键去查询的:SELECT * FROM 'students' ORDER BY 'studens'.'id' LIMIT 1

5.5 根据主键查询

注意这是前端传进来的话一定要用?拼接,不能fprintf,这样可以有效防止sql注入

5.6 根据结构体进行查询

但是注意只能根据主键查询

5.7 获取查询的结构

5.8 查询多条记录并返回Json数据

5.9 根据主键列表去查询

根据其他条件就可以

DB.Find(&studentList,"name in ?",[]string{"yuanlai","chenchen"})

06. 更新数据

有三个操作可以进行,Save,Uptate,Updates

6.1 Save 保存所有字段

用于单个记录的全字段更新,他会保存所有的字段,即使零值也会保存 

相当于:UPDATE 'students_two' SET 'name'='y145','age'=23,'gender'=true,'emaI'=2777137742@qq.com WHERE 'id'=1

Select更新指定字段

6.2 Update批量更新

或者Model

6.3 Updates更新

可以传结构体

也可以传map

07. 删除数据

08. 添加钩子函数(HOOK)

比如再插入一条数据之前,我想要做一点事情

其实就是实现一个 BeforeCreate 的方法

在实现BeforeCreate之后

插入这个

09. Gorm高级查询

9.01 Where查询

我们重构一些数据

上面用了一个函数,传string返回他的地址,这样插入的时候好看些

然后正文,这个Where就等价于mysql的where

9.02 Select 选择字段

因为直接Find是select * 比较耗费性能

9.03 排序

9.04 分页查询

先看纯sql的分页查询

9.05 去重

 先看sql去重

gorm去重(Scan就是把前面得到的结果给Scan里面的结构体)


9.06 分组查询

sql拼接名字

gorm写法

9.07 gorm执行原生sql

就上面例子来说,就是DB.Raw("saw_sql").Scan(&groupList)

9.08 子查询

子查询就是使用上次查询的结果来作为这次查询的参数

9.09 命名参数

我们之前是?,但是如果查询语句比较多,看的就不直观,orm就可以提供像@name(给参数命名)这样的方式

9.10 从Find到Map

我们每次查询的时候,都要写一个变量去接收查到的值,感觉很麻烦

(之前的 var students []Student,这个students用来Find(&students)传参的)

我们就可以用一个map来接收

emmmmmm好像没什么区别

9.11 查询引用Scope

10. 一对多关系

10.1 表结构建立

对于外键的命名,我们这里就必须要叫做UserID,其他的就不可以

或者不一样的话,我们就需要重写外键关联

重写外键关联

这就要注意,两边都要加上

10.2 添加数据

创建用户的时候创建文章

创建文章的时候,再去关联用户

又或者,但是这样就会又创建一个新的用户去关联他

又或者,不创建user,用已经有的

外键添加

其中Association和Append的方式更常用

10.3 查询数据

预加载

嵌套预加载

带条件的预加载

自定义预加载

10.4 删除数据

级联删除

清除外键关系

11. 一对一关系

表结构搭建

PS:UserInfo里面用指针是因为如果不用,就是和User相互引用了

添加记录

查询

删除

删除和一对多是一样的

12. 多对多关系

多对多关系,需要用第三张表存储两张表的关系

12.1 表结构搭建

12.2 添加

添加文章,并创建标签

查看表

创建文章,添加已有标签

12.3 查询

查询文章,显示文章的标签列表

查询标签,显示文章列表

12.4 更新

之前的方式

gorm提供的方法

让tag1替换为tag2

12.5 多对多自定义连接表(第三张表)

默认的连接表,只有双方的主键id,展示不了更多信息了,比如我们现在想要连接表里面添加数据的时候加上添加的时间,这个时候就需要自定义连接表,这就是他的意义

12.5.1 表结构及生成

注意`form:"many2many:article_tags"` article_tags这个名字是对应ArticleTag的,这个是对应的然后加上 _ 和 s,要是想改gorm里面的名字,那么AticleTag也要改

// 设置Article的Tags表为ArticleTag
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
// 如果tag要反向应用Article,那么也得加上
// DB.SetupJoinTable(&Tag{}, "Articles", &ArticleTag{})
err := DB.AutoMigrate(&Article{}, &Tag{}, &ArticleTag{})
fmt.Println(err)

12.5.2 操作案例

举一些简单的例子

  1. 添加文章并添加标签,并自动关联

  2. 添加文章,关联已有标签

  3. 给已有文章关联标签

  4. 替换已有文章的标签

  5. 添加文章并添加标签,并自动关联

1. 添加文章并添加标签,并自动关联
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})  // 要设置这个,才能走到我们自定义的连接表
DB.Create(&Article{Title: "flask零基础入门",Tags: []Tag{{Name: "python"},{Name: "后端"}, {Name: "web"},},
})
// CreatedAt time.Time 由于我们设置的是CreatedAt,gorm会自动填充当前时间,
// 如果是其他的字段,需要使用到ArticleTag 的添加钩子 BeforeCreate
2. 添加文章,关联已有标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
var tags []Tag
DB.Find(&tags, "name in ?", []string{"python", "web"})
DB.Create(&Article{Title: "flask请求对象",Tags:  tags,
})
3. 给已有文章关联标签
DB.SetupJoinTable(&Article{}, "Tags", &ArticleTag{})
article := Article{Title: "django基础",
}
DB.Create(&article)
var at Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"python", "web"})
DB.Take(&at, article.ID).Association("Tags").Append(tags)
4. 替换已有文章的标签
var article Article
var tags []Tag
DB.Find(&tags, "name in ?", []string{"后端"})
DB.Take(&article, "title = ?", "django基础")
DB.Model(&article).Association("Tags").Replace(tags)
5. 查询文章列表,显示标签
var articles []Article
DB.Preload("Tags").Find(&articles)
fmt.Println(articles)
SetupJoinTable

添加和更新的时候得用这个

这样才能走自定义的连接表,以及走它的钩子函数

查询则不需要这个

(如果添加和更新不加,就我们这个情况的话时间就不会添加上去,因为他就不会走SetupJoinTable的那张表,而是默认的那张表,而那张表里面则没有时间,所以不会添加时间,但是不会添加失败)

12.5.3 自定义连接表主键

这个功能还是很有用的,例如你的文章表 可能叫ArticleModel,你的标签表可能叫TagModel

那么按照gorm默认的主键名,那就分别是ArticleModelID,TagModelID,太长了,根本就不实用

这个地方,官网给的例子看着也比较迷,不过我已经跑通了

主要是要修改这两项

joinForeignKey 连接的主键id

JoinReferences 关联的主键id

type ArticleModel struct {ID    uintTitle stringTags  []TagModel `gorm:"many2many:article_tags;joinForeignKey:ArticleID;JoinReferences:TagID"`
}type TagModel struct {ID       uintName     stringArticles []ArticleModel `gorm:"many2many:article_tags;joinForeignKey:TagID;JoinReferences:ArticleID"`
}type ArticleTagModel struct {ArticleID uint `gorm:"primaryKey"` // article_idTagID     uint `gorm:"primaryKey"` // tag_idCreatedAt time.Time
}
生成表结构
DB.SetupJoinTable(&ArticleModel{}, "Tags", &ArticleTagModel{})
DB.SetupJoinTable(&TagModel{}, "Articles", &ArticleTagModel{})
err := DB.AutoMigrate(&ArticleModel{}, &TagModel{}, &ArticleTagModel{})
fmt.Println(err)

添加,更新,查询操作和上面的都是一样

12.5.4 操作连接表(自定义连接表的时候)

如果通过一张表去操作连接表,这样会比较麻烦

比如查询某篇文章关联了哪些标签

或者是举个更通用的例子,用户和文章,某个用户在什么时候收藏了哪篇文章

无论是通过用户关联文章,还是文章关联用户都不太好查

最简单的就是直接查连接表

type UserModel struct {ID       uintName     stringCollects []ArticleModel `gorm:"many2many:user_collect_models;joinForeignKey:UserID;JoinReferences:ArticleID"`
}type ArticleModel struct {ID    uintTitle string// 这里也可以反向引用,根据文章查哪些用户收藏了
}// UserCollectModel 用户收藏文章表
type UserCollectModel struct {UserID    uint `gorm:"primaryKey"` // article_idArticleID uint `gorm:"primaryKey"` // tag_idCreatedAt time.Time
}func main() {DB.SetupJoinTable(&UserModel{}, "Collects", &UserCollectModel{})err := DB.AutoMigrate(&UserModel{}, &ArticleModel{}, &UserCollectModel{})fmt.Println(err)
}

常用的操作就是根据用户查收藏的文章列表

var user UserModel
DB.Preload("Collects").Take(&user, "name = ?", "枫枫")
fmt.Println(user)

但是这样不太好做分页,并且也拿不到收藏文章的时间

var collects []UserCollectModel
DB.Find(&collects, "user_id = ?", 2)
fmt.Println(collects)

这样虽然可以查到用户id,文章id,收藏的时间,但是搜索只能根据用户id搜,返回也拿不到用户名,文章标题等

我们需要改一下表结构,不需要重新迁移,加一些字段

type UserModel struct {ID       uintName     stringCollects []ArticleModel `gorm:"many2many:user_collect_models;joinForeignKey:UserID;JoinReferences:ArticleID"`
}type ArticleModel struct {ID    uintTitle string
}// UserCollectModel 用户收藏文章表
type UserCollectModel struct {UserID       uint         `gorm:"primaryKey"` // article_idUserModel    UserModel    `gorm:"foreignKey:UserID"`ArticleID    uint         `gorm:"primaryKey"` // tag_idArticleModel ArticleModel `gorm:"foreignKey:ArticleID"`CreatedAt    time.Time
}

查询

var collects []UserCollectModelvar user UserModel
DB.Take(&user, "name = ?", "枫枫")
// 这里用map的原因是如果没查到,那就会查0值,如果是struct,则会忽略零值,全部查询
DB.Debug().Preload("UserModel").Preload("ArticleModel").Where(map[string]any{"user_id": user.ID}).Find(&collects)for _, collect := range collects {fmt.Println(collect)
}

13. 自定义数据类型

很多情况下我们存储到数据库中的数据是多变的

例如我需要存储json或者是数组

然后很多数据库并不能直接存储这些数据类型,我们就需要自定义数据类型

自定义的数据类型必须实现 Scanner 和 Valuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库

gorm中自定义数据类型无外乎就两个方法

在数据入库的时候要转换为什么数据,已经出库的时候数据变成什么样子

13.1 存储json

存储json可能是经常使用到的

我们需要定义一个结构体,在入库的时候,把它转换为[]byte类型,查询的时候把它转换为结构体

type Info struct {Status string `json:"status"`Addr   string `json:"addr"`Age    int    `json:"age"`
}// Scan 从数据库中读取出来
func (i *Info) Scan(value interface{}) error {bytes, ok := value.([]byte)if !ok {return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))}err := json.Unmarshal(bytes, i)return err
}// Value 存入数据库
func (i Info) Value() (driver.Value, error) {return json.Marshal(i)
}type AuthModel struct {ID   uintName stringInfo Info `gorm:"type:string"`
}func main() {DB.AutoMigrate(&AuthModel{})
}

插入数据

DB.Debug().Create(&AuthModel{Name: "枫枫",Info: Info{Status: "success",Addr:   "湖南省长沙市",Age:    21,},
})// INSERT INTO `auth_models` (`name`,`info`) VALUES ('枫枫','{"status":"success","addr":"湖南省长沙市","age":21}')

查询数据

var auth AuthModel
DB.Take(&auth, "name = ?", "枫枫")
fmt.Println(auth)

13.2 存储数组

很多时候存储数组也是很常见的

最简单的方式就是存json

type Array []string// Scan 从数据库中读取出来
func (arr *Array) Scan(value interface{}) error {bytes, ok := value.([]byte)if !ok {return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))}err := json.Unmarshal(bytes, arr)return err
}// Value 存入数据库
func (arr Array) Value() (driver.Value, error) {return json.Marshal(arr)
}type HostModel struct {ID    uint   `json:"id"`IP    string `json:"ip"`Ports Array  `gorm:"type:string" json:"ports"`
}func main() {//DB.AutoMigrate(&HostModel{})//DB.Create(&HostModel{//  IP:    "192.168.200.21",//  Ports: []string{"80", "8080"},//})var host HostModelDB.Take(&host, 1)fmt.Println(host)
}

当然,也可以用字符串拼接,例如 |, =,  , 

type Array []string// Scan 从数据库中读取出来
func (arr *Array) Scan(value interface{}) error {data, ok := value.([]byte)if !ok {return errors.New(fmt.Sprintf("解析失败: %v %T", value, value))}*arr = strings.Split(string(data), "|")return nil
}// Value 存入数据库
func (arr Array) Value() (driver.Value, error) {return strings.Join(arr, "|"), nil
}

当然,拼接的字符串不能是输入字符串中存在的

13.3 枚举类型

枚举1.0

很多时候,我们会对一些状态进行判断,而这些状态都是有限的

例如,主机管理中,状态有 Running 运行中, OffLine 离线, Except 异常

如果存储字符串,不仅是浪费空间,每次判断还要多复制很多字符,最主要是后期维护麻烦

type Host struct {ID     uintName   stringStatus string
}func main() {host := Host{}if host.Status == "Running" {fmt.Println("在线")}if host.Status == "Except" {fmt.Println("异常")}if host.Status == "OffLine" {fmt.Println("离线")}
}

后来,我们知道了用常量存储这些不变的值

type Host struct {ID     uintName   stringStatus string
}const (Running = "Running"Except = "Except"OffLine = "OffLine"
) func main() {host := Host{}if host.Status == Running {fmt.Println("在线")}if host.Status == Except {fmt.Println("异常")}if host.Status == OffLine {fmt.Println("离线")}
}

虽然代码变多了,但是维护方便了

但是数据库中存储的依然是字符串,浪费空间这个问题并没有解决

枚举2.0

于是想到使用数字表示状态

type Host struct {ID     uintName   stringStatus int
}const (Running = 1Except  = 2OffLine = 3
)func main() {host := Host{}if host.Status == Running {fmt.Println("在线")}if host.Status == Except {fmt.Println("异常")}if host.Status == OffLine {fmt.Println("离线")}
}

但是,如果返回数据给前端,前端接收到的状态就是数字,不过问题不大,前端反正都要搞字符映射的

因为要做颜色差异显示

但是这并不是后端偷懒的理由

于是我们想到,在json序列化的时候,根据映射转换回去

type Host struct {ID     uint   `json:"id"`Name   string `json:"name"`Status int    `json:"status"`
}func (h Host) MarshalJSON() ([]byte, error) {var status stringswitch h.Status {case Running:status = "Running"case Except:status = "Except"case OffLine :status = "OffLine"}return json.Marshal(&struct {ID     uint   `json:"id"`Name   string `json:"name"`Status string `json:"status"`}{ID:     h.ID,Name:   h.Name,Status: status,})
}const (Running = 1Except  = 2OffLine  = 3
)func main() {host := Host{1, "枫枫", Running}data, _ := json.Marshal(host)fmt.Println(string(data)) // {"id":1,"name":"枫枫","status":"Running"}
}

这样写确实可以实现我们的需求,但是根本就不够通用,凡是用到枚举,都得给这个Struct实现MarshalJSON方法

枚举3.0(用这个)

于是类型别名出来了

type Status intfunc (status Status) MarshalJSON() ([]byte, error) {var str stringswitch status {case Running:str = "Running"case Except:str = "Except"case OffLine:str = "Status"}return json.Marshal(str)
}type Host struct {ID     uint   `json:"id"`Name   string `json:"name"`Status Status `json:"status"`
}const (Running Status = 1Except  Status = 2OffLine Status = 3
)func main() {host := Host{1, "枫枫", Running}data, _ := json.Marshal(host)fmt.Println(string(data)) // {"id":1,"name":"枫枫","status":"Running"}
}

嗯,代码简洁了不少,在使用层面已经没有问题了

在grom中使用

type Status intfunc (s Status) MarshalJSON() ([]byte, error) {return json.Marshal(s.String())
}func (s Status) String() string {var str stringswitch s {case Running:str = "Running"case Except:str = "Except"case OffLine:str = "Status"}return str
}const (Running Status = 1OffLine Status = 2Except  Status = 3
)type Host struct {ID     uint   `json:"id"`Status Status `gorm:"size:8" json:"status"`IP     string `json:"ip"`
}func main() {//DB.AutoMigrate(&Host{})//DB.Create(&Host{//  IP:     "192.168.200.12",//  Status: Running,//})var host HostDB.Take(&host)fmt.Println(host)fmt.Printf("%#v,%T\n", host.Status, host.Status)data, _ := json.Marshal(host)fmt.Println(string(data))}

14. gorm 事务

事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。

很形象的一个例子,张三给李四转账100元,在程序里面,张三的余额就要-100,李四的余额就要+100
整个事件是一个整体,哪一步错了,整个事件都是失败的

gorm事务默认是开启的。为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。

如果没有这方面的要求,您可以在初始化时禁用它,这将获得大约 30%+ 性能提升。

一般不推荐禁用

// 全局禁用
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{SkipDefaultTransaction: true,
})

本节课表结构

type User struct {ID    uint   `json:"id"`Name  string `json:"name"`Money int    `json:"money"`
}// InnoDB引擎才支持事务,MyISAM不支持事务
// DB.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})

14.1 普通事务

以张三给李四转账为例,不使用事务的后果

var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")
// 张三给李四转账100元
// 先给张三-100
zhangsan.Money -= 100
DB.Model(&zhangsan).Update("money", zhangsan.Money)
// 模拟失败的情况// 再给李四+100
lisi.Money += 100
DB.Model(&lisi).Update("money", lisi.Money)

在失败的情况下,要么张三白白损失了100,要么李四凭空拿到100元

这显然是不合逻辑的,并且不合法的

那么,使用事务是怎样的

var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")
// 张三给李四转账100元
DB.Transaction(func(tx *gorm.DB) error {// 先给张三-100zhangsan.Money -= 100err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Errorif err != nil {fmt.Println(err)return err}// 再给李四+100lisi.Money += 100err = tx.Model(&lisi).Update("money", lisi.Money).Errorif err != nil {fmt.Println(err)return err}// 提交事务return nil
})

使用事务之后,他们就是一体,一起成功,一起失败

14.2 手动事务

// 开始事务
tx := db.Begin()// 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
tx.Create(...)// ...// 遇到错误时回滚事务
tx.Rollback()// 否则,提交事务
tx.Commit()

刚才的代码也可以这样实现

var zhangsan, lisi User
DB.Take(&zhangsan, "name = ?", "张三")
DB.Take(&lisi, "name = ?", "李四")// 张三给李四转账100元
tx := DB.Begin()// 先给张三-100
zhangsan.Money -= 100
err := tx.Model(&zhangsan).Update("money", zhangsan.Money).Error
if err != nil {tx.Rollback()
}// 再给李四+100
lisi.Money += 100
err = tx.Model(&lisi).Update("money", lisi.Money).Error
if err != nil {tx.Rollback()
}
// 提交事务
tx.Commit()

本篇文章根据 小破站 枫枫知道 所著,也是作者很喜欢的博主哈~

创作不易,希望读者三连支持 💖
赠人玫瑰,手有余香 💖

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

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

相关文章

Vue3学习03 pinia

Vue3学习 pinia pinia一个简单效果搭建 pinia 环境存储读取数据示例 修改数据 (三种方式)storeToRefsgetters$subscribestore组合式写法 pinia 在vue2中使用vuex,在vue3中使用pinia。 集中式状态管理,(状态数据)。多个组件共享数…

多态【C/C++复习版】

目录 一、多态是什么?如何实现? 二、 什么是重写?有什么特点? 三、什么是协变? 四、析构函数能实现多态吗?为什么要实现? 五、override和final的作用是什么? 六、 多态的原理是…

Linux下网络编程基础知识--协议

网络基础 这一个课程的笔记 相关文章 协议 Socket编程 高并发服务器实现 线程池 协议 一组规则, 数据传输和数据的解释的规则。 比如说依次发送文件的文件名, 文件的大小, 以及实际的文件, 这样规定发送一个文件的顺序以及发送的每一个部分的格式等可以算是一种协议 型协议 …

防火墙用户管理技术——AAA

目录 一.AAA功能 (1).认证方式 (2).授权方式 (3).计费方式 二.RADUIUS协议 三.用户组织架构及管理 管理员认证登录方式 1.console 2.web 3.telnet 4.ssh 5.ftp 四.认证方式 五.AAA远程登录 1.端口添加IP地址达到互通 2.AAA视图进行配置 3.结果​编辑 一.AAA功能…

Android源码解析之截屏事件流程

今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程。用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了)。那么这里的截屏事件是如何触发的呢…

【Redis深度解析】揭秘Cluster(集群):原理、机制与实战优化

Redis Cluster是Redis官方提供的分布式解决方案,通过数据分片与节点间通信机制,实现了水平扩展、高可用与数据容灾。本文将深入剖析Redis Cluster的工作原理、核心机制,并结合实战经验分享优化策略,为您打造坚实可靠的Redis分布式…

双数据库的安装

双MySQL的安装 【0】前言 ​ 本地已经安装过mysql5.1版本,应项目需求需要安装mysql5.7版本; ​ 官方网站下载对应版本:https://downloads.mysql.com/archives/community/ 【1】压缩包下载完成后解压至本地磁盘 【2】进入根目录下bin文件夹…

Flask基于flask_login实现登录、验证码

flask_login 是一个 Flask 扩展,用于在 Flask web 应用中实现用户会话管理。它允许你跟踪哪些用户已经登录,并管理他们的登录状态。flask_login 提供了用户认证的基础结构,但具体的用户验证(如用户名和密码检查)和存储…

泽众Testone自动化测试平台,测试用例支持单个调试执行,同步查看执行日志

泽众Testone自动化测试平台之前版本,测试用例批量和单个执行,必须要通过测试集操作执行,操作略繁琐,我们通过本轮优化升级,测试用例直接可以单个调试执行,同步查看执行日志,操作上去繁就简&…

OJ刷题日记:1、双指针(1)

目录 1、283.移动零 2、1089.复写零 3、202.快乐数 1、283.移动零 题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 …

第十届 蓝桥杯 单片机设计与开发项目 省赛

第十届 蓝桥杯 单片机设计与开发项目 省赛 输入: 频率信号输入模拟电压输入 输出(包含各种显示功能): LED显示SEG显示DAC输出 01 数码管显示问题:数据类型 bit Seg_Disp_Mode;//0-频率显示界面 1-电压显示界面 un…

电脑无法开机?原因分析与解决方案

电脑无法开机是一种常见的问题,可能会给用户带来诸多困扰。无法启动可能是由于硬件故障、软件问题或者其他未知原因引起的。在本文中,我们将介绍三种常见的方法来解决电脑无法开机的问题,以帮助用户尽快恢复正常使用。 方法1:检查…

什么是队列

队列是一种特殊类型的线性表,其只允许在一端进行插入操作,而在另一端进行删除操作。具体来说,允许插入的一端称为队尾,而允许删除的一端称为队头。这种数据结构遵循“先进先出”(FIFO)的原则,即…

ubuntu安装irtualbox注意事项

下载官网7.0版本,ubuntu22.04,安装一直出错误,查到了下面兄弟的办法,仍然不幸,最后使用apt重装,请按第二部分流程安装,安装6.1,可以用 第一部分,反正我是没搞通,7.0反复的出现相同的…

C语言读取 .ico 文件并显示数据

原来是想做光标编辑器&#xff0c;自己把绘图板的内容导出为光标格式 鼠标指针文件格式解析——Windows&#xff08;一&#xff09; (qq.com) 代码来源自 Icons | Microsoft Learn 鄙人又补充些变量可以运行微软的代码 简单代码如下 #include <stdio.h> #include &l…

神经网络模型底层原理与实现8-BERT

首先介绍什么是自监督学习&#xff1a; 普通的有监督学习是每个x对应有个y&#xff0c;x训练得到y&#xff0c;将y与y作比较&#xff0c;而自监督是没有对应y&#xff0c;直接把一部分样本x作为训练目标x&#xff0c;训练得x后和x对比 bert中如何创造x&#xff1a;以文字处理为…

4/11 QT_day5

服务器端 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QMessageBox> #include <QTcpSocket> #include <QList> QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass …

MSTP/RSTP与STP的兼容性

原理概述 MSTP(Multiple STP)协议和RSTP(Rapid STP)协议都可以向下兼容STP&#xff08;Spanning Tree Protocol &#xff09;协议。运行MSTP/RSTP协议的交换机会根据收到的BPDU版本号信息自动判断与之相连的交换机的运行模式。如果收到的是STP BPDU&#xff0c;MSTP/RSTP交换机…

Oracle 21c 数据库迁移到DM8(达梦)数据库

一、环境准备 1、创建脚本 执行dmCreateUser.sql脚本创建GLJ用户&#xff08;注意&#xff1a;需要与需要迁移的oracle用户名一样&#xff09;&#xff0c;如&#xff0c;脚本内容如下&#xff1a; -- 开始将输出重定向到指定的日志文件 spool start /home/dmdba/dmdbms/sql/…

【Linux】sudo分权管理实战

一般sudo命令是默认安装的&#xff0c;如果你的机器里没有&#xff0c;可以使用命令 yum install sudo 来安装 [rootgaosh-64 ~]# yum install sudo 我们来看一下配置文件&#xff1a; 在上图root下面添加一行 &#xff0c;给gaosh用户加权限 [rootgaosh-64 ~]# vim /etc/su…