山东系统建站怎么用,中能建设集团电子商务网站,免费logo设计图,网站域名怎么写**本人是第六届字节跳动青训营#xff08;后端组#xff09;的成员。本文由博主本人整理自该营的日常学习实践#xff0c;首发于稀土掘金。 我的go开发环境#xff1a; *本地IDE#xff1a;GoLand 2023.1.2 *go#xff1a;1.20.6 *MySQL#xff1a;8.0 本文介绍Go框架三…**本人是第六届字节跳动青训营后端组的成员。本文由博主本人整理自该营的日常学习实践首发于稀土掘金。 我的go开发环境 *本地IDEGoLand 2023.1.2 *go1.20.6 *MySQL8.0 本文介绍Go框架三件套的使用。
Go 框架三件套通常指的是 GORM、Kitex 和 Hertz它们分别是 Go 语言中数据库 ORM 库、分布式微服务框架和 Web 框架。
下面我们来逐个介绍各框架的使用。
GORM数据库 ORM 库
GORM 官方文档https://gorm.cn/zh_CN/docs/connecting_to_the_database.html
1、GORM的简介
GORM 是 Go 语言中一个强大的 ORM 库ORM 即对象关系映射是一种程序设计技术用于将不同类型的数据进行转换从而实现面向对象编程语言与数据库之间的交互用于简化数据库操作。
它目前支持 MySQL、SQLServer、PostgreSQL、SQLite提供了一系列便捷的 API 用于进行数据库的增删改查等操作还支持模型定义、关联查询等高级功能。
GORM 是通过驱动的方式来连接数据库的 GORM 的基本使用步骤如下
安装 GORM使用 go get 命令获取 GORM 包及数据库驱动。连接数据库导入 GORM 包并创建数据库连接例如连接 SQLite。定义模型创建结构体来定义数据库表与字段的映射关系。自动迁移使用 AutoMigrate 方法自动创建或更新数据库表结构。数据操作使用 Create、First、Where 等方法进行增删改查操作。
本文以 MySQL 数据库为例进行演示。
2、GORM的基本使用以MySQL为例
我们先来看一下总体的GORM的使用 *GORM的约定默认情况
GORM使用名为ID的字段作为主键。未给model定义表名时使用结构体的蛇形复数作为表名。字段名的蛇形作为列名。使用 CreatedAt、UpdatedAt 字段作为创建更新时间。 蛇形命名 在数据库中蛇形命名Snake Case是一种命名约定用于在标识符中使用下划线_分隔单词。蛇形命名的主要特点是所有单词都小写并且使用下划线分隔例如user_info、order_details。与蛇形命名相对的是驼峰命名Camel Case。 复数形式 在 GORM 中默认情况下如果结构体名称是单数形式例如 User那么 GORM 会将其转换为表名的复数形式例如 users。这个行为是 GORM 的默认命名约定之一目的是在数据库中使用表的复数形式来存储多条记录。 这种默认的复数形式表名的转换规则是为了遵循一些数据库的命名习惯同时也有助于避免表名与保留关键字冲突。 步骤 1安装 GORM 和 MySQL 驱动
首先打开Goland终端使用 go get 命令安装 GORM 包和 MySQL 驱动
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql步骤 2连接数据库
安装好驱动后可以在 Go 代码中导入 GORM 包和 MySQL 驱动并创建数据库连接
package mainimport (gorm.io/driver/mysqlgorm.io/gormlog
)func main() {dsn : user:passwordtcp(host:port)/database?charsetutf8mb4parseTimeTruelocLocaldb, err : gorm.Open(mysql.Open(dsn), gorm.Config{})if err ! nil {log.Fatal(无法连接数据库)}// 在这里可以使用 db 进行数据库操作
}DSN中包含了数据库连接相关的信息。MySQL的DSN说明文档
将上面的代码中DSN的 user、password、host、port、database 分别替换为实际要连接的 MySQL 数据库的信息即可连接成功。
步骤 3定义模型和操作数据库
在连接数据库之后定义数据模型 model 并使用 GORM 进行数据库操作。
以下是一个简单的示例演示了如何定义一个 User 模型并进行插入、查询、更新和删除操作
package mainimport (gorm.io/driver/mysqlgorm.io/gormlog
)type User struct {ID uint gorm:primaryKeyName stringAge int
}func main() {dsn : user:passwordtcp(host:port)/database?charsetutf8mb4parseTimeTruelocLocaldb, err : gorm.Open(mysql.Open(dsn), gorm.Config{})if err ! nil {log.Fatal(无法连接数据库)}db.AutoMigrate(User{})// 插入数据newUser : User{Name: Alice, Age: 25}db.Create(newUser)// 查询数据var user Userdb.First(user, 1)// 更新数据db.Model(user).Update(Age, 26)// 删除数据db.Delete(user)
}db.AutoMigrate(User{}) 是 GORM 提供的一个方法用于自动创建或更新数据库表结构以使其与你定义的模型保持一致。它在应用程序启动时非常有用可以确保数据库表的结构与代码中定义的模型一致而无需手动创建表或执行 SQL 脚本。 具体来说AutoMigrate 方法会根据你定义的模型结构通过结构体字段的标签生成相应的数据库表并且可以自动处理新增的字段、修改的字段类型、删除的字段等变化。这在开发过程中特别有用因为你可以在模型结构中进行更改然后通过自动迁移保持数据库表结构的更新。 在上述示例中当应用程序启动时db.AutoMigrate(User{}) 会检查数据库中是否存在名为 users 的表如果不存在则创建这张表字段与 User 结构体中的定义保持一致。如果已存在表GORM 会根据模型定义的变化自动更新表结构例如新增、修改、删除字段。 更改DSN的信息后在本地运行上述代码观察结果
// 插入数据
newUser : User{Name: Alice, Age: 25}
db.Create(newUser)// 查询数据
var user User
db.First(user, 1)fmt.Printf(%v\n, user)//%v 格式化占位符会打印出结构体的字段名和对应的值以便更清晰地查看结构体的内容。控制台打印查询结果 注意在实际使用中请务必妥善保管数据库的敏感信息如用户名、密码等避免泄露。
//要先通过查询的操作将数据库中某一字段写入model以建立起关联
//该代码查询出数据库中主id为6的记录
var user User
db.First(user, 6)
// 更新数据
db.Model(user).Update(Age, 26)在 GORM 中如果想要更新数据库中的数据首先需要通过查询语句获取到要更新的数据并将获取到的结果赋值给相应的结构体变量然后再使用 Update 或 Updates 方法来进行实际的更新操作。 // 删除数据
db.Delete(user)将指定数据删除 总之使用 GORM 连接 MySQL 数据库非常简单只需要按照上述步骤导入相关包并配置数据库连接信息就能够使用 GORM 提供的便捷 API 进行数据库操作。
3、GORM主要API详解
GORM中各个API是链式调用的。Where()的工作只是拼接sql而Create()First()Update()Delet()等才是真正执行sql的。如果顺序颠倒调用完后者再去调用Where()那么Where条件是不会生效的。
1GORM创建数据 Create()
Create()方法可以创建一条数据也可以批量创建数据。
创建一条数据记录
newUser : User{Name: Alice, Age: 25} //创建结构体
result : db.Create(newUser)if result.Error ! nil {log.Fatal(无法创建数据, result.Error)
}批量创建多条数据记录
在 GORM 中要使用 Create 方法批量创建数据需要将多个数据记录组成切片然后使用 Create 方法进行批量插入。
users : []User{{Name: Alice, Age: 25},{Name: Bob, Age: 30},{Name: Charlie, Age: 28},
}
result : db.Create(users)if result.Error ! nil {log.Println(无法创建数据, result.Error)
}青训营培训中李龙讲师给出的代码解析如下 使用 clause.OnConflict 处理数据冲突 如何使用默认值
通过使用 default 标签为字段定义默认值。 2GORM查询数据 First() Find()
查询一条数据
First() 方法用于查询数据库中的第一条符合条件的记录并将结果存储在指定的结构体变量中。默认按照主键顺序进行查询通常为创建记录时的顺序。如果需要按照其他字段排序可以使用 Order 方法进行排序。
这个方法适用于需要查询一条记录的情况。
以下是 First() 方法的基本用法
db.First(user, 1)db是一个 GORM 数据库连接实例。First是 GORM 提供的查询方法。user表示要将查询结果存储到名为 user 的结构体变量中。注意这里使用了 操作符表示传递了 user 变量的内存地址以便在查询结果中存储数据。1是查询的条件这里表示按照主键为 1 进行查询。
First() 方法执行后会从数据库中获取满足条件的第一条数据记录并将结果存储在 user 变量中。如果没有找到符合条件的记录user 变量将保持不变即空的结构体值会返回 ErrRecordNotFound
如果需要根据其他条件进行查询可以使用 GORM 的查询条件构造方法例如 Where、Or 等来构建查询条件
基本的条件查询
// 查询 name 为 Alice 的第一条记录
db.Where(name ?, Alice).First(user)AND 和 OR 条件查询
// 查询 age 大于等于 25 并且 name 不为 Bob 的记录
db.Where(age ? AND name ?, 25, Bob).Find(users)// 查询 age 小于 30 或者 name 为 Alice 的记录
db.Where(age ? OR name ?, 30, Alice).Find(users)IN 条件查询
// 查询 age 在给定的列表 [25, 30] 中的记录
ages : []int{25, 30}
db.Where(age IN ?, ages).Find(users)LIKE 条件查询
// 查询 name 包含 li 的记录
db.Where(name LIKE ?, %li%).Find(users)其他查询条件
GORM 还提供了许多其他的查询条件构造方法如 Not、Or、Between、IsNull、NotNull 等可以根据需要选择合适的条件方法来构建复杂的查询。
查询一组数据
在 GORM 中Find() 方法用于执行查询并从数据库中检索满足指定条件的多条记录。它会将查询结果存储到指定的切片或数组中。Find() 方法适用于需要查询多条记录的情况例如根据某个条件查找多个数据记录。
以下是 Find() 方法的基本用法
db.Find(users)Find是 GORM 提供的查询方法。users表示要将查询结果存储到名为 users 的切片或数组中。注意这里使用了 操作符表示传递了 users 变量的内存地址以便在查询结果中存储数据。
Find() 方法执行后会从数据库中获取满足条件的多条数据记录并将结果存储到 users 变量中。如果没有找到符合条件的记录users 变量将保持为空切片或空数组。
需要注意的是当使用 Find() 方法时需要确保目标切片或数组的元素类型与数据库中的记录结构体类型相匹配。
培训中代码解析 为什么要创建一个指向结构体的指针的切片
在 GORM 中查询多组数据通常会使用切片来存储查询结果。而为什么要创建一个指向结构体的指针的切片涉及到 Go 语言中切片和结构体的内存管理以及 GORM 的工作机制。
切片的引用语义 在 Go 语言中切片是引用类型。这意味着当你将切片传递给函数或方法时实际上传递的是切片的引用而不是它的拷贝。当切片被修改时所有引用这个切片的地方都会受到影响。因此使用切片可以在多个地方共享数据而无需进行显式的复制。
指向结构体的指针 在 GORM 中查询结果需要映射到特定的结构体上。由于 GORM 需要修改结构体字段的值以反映数据库的实际数据所以在查询结果存储时必须传递结构体的指针。这样 GORM 才能直接修改结构体的字段。
因此当使用 GORM 查询多组数据时需要创建一个指向结构体的指针的切片以便将查询结果存储在切片中。这样切片中的每个元素都指向一个具体的结构体实例而 GORM 可以直接修改这些实例的字段来填充查询结果。
在示例代码中users : make([]*User, 0) 创建了一个初始为空的指向 User 结构体的指针的切片。然后通过 db.Where(query:age 0).Find(users) 将查询结果存储到这个切片中。这样就可以在切片中得到查询的多组数据并且每个元素都是一个指向 User 结构体的指针GORM 可以使用这些指针来填充查询结果。
First 的使用踩坑
使用 First 时需要注意查询不到数据会返回 ErrRecordNotFound。使用 Find 查询多条数据查询不到数据不会返回错误。更多的是使用Find然后自己通过判断处理是否查询到数据。使用结构体作为查询条件。当使用结构作为条件查询时GORM只会查询非零值字段。这意味着如果您的字段值为 0、、false 或其他零值该字段不会被用于构建查询条件使用Map 来构建查询条件。
3GORM更新数据 Update() Updates() 注意
使用 Struct 进行更新时只会更新非零值如果需要更新零值可以使用 Map 更新或使用Select 选择字段。map[string]interface{} 是 Go 语言中的一种数据结构用于存储键值对其中键是字符串类型值是空接口类型。这种数据结构允许你在一个 map 中存储不同类型的值。
解释一下其中的各个部分
map是 Go 语言中的一种内置数据结构用于存储键值对。每个键在 map 中必须是唯一的。string表示 map 的键的数据类型这里是字符串类型。interface{}表示 map 的值的数据类型这里是空接口类型。空接口可以存储任何类型的值因为所有类型都满足空接口类型。
使用 map[string]interface{} 时可以将任何类型的值与字符串键关联起来。这在一些动态的情况下非常有用比如需要在一个 map 中存储不同类型的配置项或数据。
以下示例演示了如何使用 map[string]interface{} 存储不同类型的值
package mainimport fmtfunc main() {// 创建一个 map键是字符串值是空接口类型data : make(map[string]interface{})// 存储不同类型的值data[name] Alicedata[age] 25data[isStudent] true// 输出 map 中的值fmt.Println(data[name])fmt.Println(data[age])fmt.Println(data[isStudent])
}4GORM删除数据 Delete()
物理删除 软删除
以往我们实现软删的思路通常是设定一个flagflag为1时没有删除flag为0是删除。但GORM中已经给我们提供了一个比较完备的软删方案。
GORM 提供了 gorm.DeletedAt 用于帮助用户实现软删拥有软删除能力的 Model 调用 Delete 时记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间并且你不能再通过正常的查询方法找到该记录。
而使用 Unscoped 可以查询到被软删的数据。 4、GORM事务
Gorm 提供了 Begin、Commit、Rollback 方法用于使用事务。 在 GORM 中使用 tx : db.Begin() 开启事务后建议后续的数据库操作都使用 tx 来调用而不是使用 db。这是因为事务对象 tx 是从数据库连接对象 db 衍生出来的它继承了数据库连接的属性并且使用事务对象 tx 可以确保数据库操作的一致性、原子性和性能同时简化错误处理。在事务中进行操作可以保证这些操作在同一个数据库事务内执行从而避免了许多潜在的并发问题。
事务的原子性 在事务中进行的所有数据库操作要么全部成功提交要么全部回滚。这确保了操作的原子性避免了因为其中一个操作失败而导致数据库数据的不一致性。性能优化 在事务中进行操作会优化数据库连接的使用。事务通常会使用数据库连接池中的一个连接而不是每个操作都创建新的连接。这可以降低资源消耗提高性能。数据一致性 使用 tx 调用后续的数据库操作可以确保这些操作在同一个事务内执行。这意味着在事务中的操作都是基于同一个数据库快照避免了因为并发操作而导致数据不一致的问题。错误处理 使用 tx 可以更方便地处理错误。如果在事务中的任何操作出现错误可以直接调用 tx.Rollback() 来回滚整个事务。而使用 db 进行操作时则需要在出现错误时手动处理回滚。
5、GORM Hook
GORM 在 提供了 CURD 的 Hook 能力。Hook 是在创建、查询、更新、删除等操作之前、之后自动调用的函数。
如果任何 Hook 返回错误GORM 将停止后续的操作并回滚事务。 6、GORM性能提高
对于写操作创建、更新、删除为了确保数据的完整性GORM 会将它们封装在事务内运行但这会降低性能可以使用 SkipDefaultTransaction 关闭默认事务。使用 PrepareStmt 缓存预编译语句可以提高后续调用的速度本机测试提高大约 35 %左右。 其他的性能优化方案见官方文档GORM性能提高
7、GORM生态
GORM 拥有非常丰富的扩展生态以下列举一部分常用扩展
GORM 代码生成工具https://github.com/go-gorm/gen
GORM 分片库方案https://github.com/go-gorm/sharding
GORM 手动索引https://github.com/go-gorm/hints
GORM 乐观锁https://github.com/go-gorm/optimisticlock
GORM 读写分离https://github.com/go-gorm/dbresolver
GORM OpenTelemetry 扩展https://github.com/go-gorm/opentelemetry
关于更多的 GORM 用法可以查看 Gorm 的文档(https://gorm.cn)。