这是我参与「第五届青训营」笔记创作活动的第5天。
今天主要是对GORM进行了学习,主要学习了Gorm框架的一些操作:连接数据库、模型迁移、增删改查。并学习了Gorm操作中的一些细节问题,如:零值更新等。
ORM
ORM(Object Relation Mapping)通过实例化对象完成对关系型数据库的操作。
连接数据库
连接一个mysql数据库:
func main() {
dsn := "root:123456@tcp(127.0.0.1:3308)/gin_learn?charset=utf8mb4&parseTime=True&loc=Local"
_, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
fmt.Println("连接成功...")
}
解释以下其中的dsn字符串:
DSN (Data source name) 包含数据库连接的主机地址、端口号、用户名、密码、数据库名等信息,以及一些配置信息,如字符集、时区等。DSN信息的格式如下:
用户名:密码@tcp(ip地址:端口)/数据库名称?charset=utf8mb4&parseTime=True&loc=Local
logger
-
Gorm 有一个 默认 logger 实现,默认情况下,它会打印慢 SQL 和错误。
-
并且GORM 定义了这些日志级别:Silent、Error、Warn、Info。
-
参考 GORM 的 默认 logger 来定义自己的 logger,Logger 需要实现以下接口,它接受
context,所以你可以用它来追踪日志
gorm 模型迁移
使用AutoMigrate函数可以将做好的模型迁移到数据库,如果当前数据库不具有该模型,则生成一个表。
// Product 声明一个商品模型
type Product struct {
gorm.Model
Code string
Price uint
}
err = db.AutoMigrate(&model.Product{})
这里根据模型生成的表结构中,列都是小写下划线形式的,例如code,create_at
gorm 增删改查
我们将连接数据库生成的db作为包级别的首先声明:
var db *gorm.DB
然后定义init函数来初始化数据库:
Go语言有一个特殊的函数init,先于main函数执行,实现包级别的一些初始化操作。
对于init 函数来说:每个包可以包含任意多个 init 函数,这些函数都会在程序执行开始的时候被调用。所有被编译器发现的 init 函数都会安排在 main 函数之前执行。init 函数用在设置包、初始化变量或其他要在程序运行前优先完成的引导工作。
init函数go语言会自动执行,我们不需要引用它。
在main函数中,我们执行增删改查的动作:
GORM的增删查改操作在官方文档中有详细的代码Demo,下面是一些课程中自己的心得体会:
-
在查询操作中,如果使用
First方法,当查询不到时会返回ErrRecordNotFound,而使用Find方法时不会出现报错,因此尽量使用Find方法进行数据查询,通过逻辑代码判断是否查询成功; -
使用结构体进行更新操作时只会更新非零值,因此需要更新零值时可以使用Map或者Select选择,一般结构体用于兜底;
gorm 更新零值的第二种方式
在上面的代码中,我们可以使用map[string]interface{}{...}更新零值,现在我们也可以使用sql.NullString这样的方式去更新零值或空值:
空字符串:sql.NullString
数字0:sql.NullInt32
gorm 模型定义
我们使用gorm时,尽量遵守其模型定义规范,官方文档中提到:
GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间
例子:
// Food 声明一个食物模型
type Food struct {
FoodId uint `gorm:"primaryKey"`
Name string `gorm:"column:food_name;type:varchar(64);index:idx_food_name,unique"`
DeletedAt gorm.DeletedAt
}
其中的FoodId作为主键;Name作为索引,在数据库中的列名为food_name,unique代表将列定义为唯一键;DeleteAt为gorm.DeletedAt类型可以用作软删除。
gorm 批量添加
方案1:
创建一个model切片,使用db.Create添加:
foods := []model.Food{
{
Name: "feed",
},
{
Name: "banana",
},
{
Name: "orange",
},
}
res := db.Create(foods) // 注意这里不使用指针,因为切片本身就是引用类型
方案2:
// 批量添加 每次添加2条
db.CreateInBatches(foods, 2)
这里的批量添加会每次向数据库中添加两条记录,如果一共有3条记录,那么会添加两次:
方案3:
使用map添加。
使用map这种方式批量添加只会添加定义的列和主键,其他的列不会使用默认值而是直接为NULL
gorm 局部更新
gorm在使用Updates方法时,gorm会自动忽略没有赋值或者赋值为零值的属性。当我们想只局部更新某些属性时,可以使用Select方法选择部分属性进行更新:
func main() {
p := &model.Product{}
db.First(&p, "code = ?", "D42").Select("price").Updates(model.Product{
Code: "xxx",
Price: 200,
})
}
当我们确定好了更新范围时,即使后面的更新函数中写明了要更新其他列,sql语句中也只会更新范围内的列:
UPDATE `products` SET `updated_at`='2023-01-01 15:34:57.693',`price`=200 WHERE code = 'D42' AND `products`.`deleted_at` IS NULL AND `id` = 2 ORDER BY `products`.`id` LIMIT 1
除了使用Select选择之外,还可以使用Omit进行忽略操作。
小结
今天对GORM进行了学习,主要学习了Gorm框架的一些操作:连接数据库、模型迁移、增删改查等。通过录播课和查阅官方文档熟悉了Gorm的基本使用,但是在后续的学习中Grom的使用细节仍需要进一步积累。