GORM学习| 青训营笔记

154 阅读5分钟

这是我参与「第五届青训营」笔记创作活动的第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 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAtUpdatedAt 字段追踪创建、更新时间

例子:

// 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的使用细节仍需要进一步积累。