这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记。
该篇笔记主要记录了gorm的crud操作,由于我是参加青训营才接触到的go,并且在项目中频繁使用到了。主要内容参考了官方文档和项目中的一些总结。
1.增加数据操作(C)
//代码1.1
db.Create(&model)
上述(代码1.1)是增加数据的基本语句,model是我们自定义的一个结构体,并且在数据库中需要有一份表与它对应,即数据库的字段和结构体的字段必须吻合,否则会报错。比如在项目实现过程中,我定义了如下结构体(代码1.2),但是设计表的时候,不小心把created_at,updated_at以及deleted_at这三个字段设置成了int,导致最终报了truncated的错误。 另外, 在为结构体定义变量名的时候,一定要避免mysql中的关键子。 由于结构体是和数据表一一对应的,在查询某一个字段会写出where 字段 = ?的语句,当字段是关键字的时候会报错。在项目实践过程中不幸遇到过并排错了好久...
//代码1.2
type Relation struct {
gorm.Model
UserId uint64 `json:"user_id" gorm:"not null"`
FocusUserId uint64 `json:"focus_user_id" gorm:"not null"`
Status uint `json:"status"`
}
在创建新数据的时候,有的时候并不是需要给所有字段都创建值的,这是可以选择Select和Obmit分别实现选择特定字段和忽略特定字段(见代码1.3)。
//代码1.3
//只选择relation中的user字段和video字段创建
db.Select("user", "video", "CreatedAt").Create(&relation)
//忽视CreatedAt字段
db.Omit("CreatedAt").Create(&relation)
在这次项目中,我频繁使用到了Select,在进行更新的操作时还是很适用的。
目前为止,上述两类创建数据是我做项目时使用频率较高的创建方式。通过阅读文档,创建数据还可以使用map传入待插入数据(个人还是喜欢前者)。
对于批量插入,gorm也是很方便的,只需要在Create方法中传入对应结构体的切片即可,如下:
//代码1.4(来自官网)
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
到目前为止,以上添加数据操作是我在项目中频繁使用并且比较熟悉的方式。另外,在官方文档中还介绍了关联创建这个概念,即一个结构体中嵌套着另一个结构体,对应数据库就是一张表的某个字段其实映射到了另一张表的某条记录,如下。但是在实际实践中,应该更加倾向于将创建两张表,即代码1.5中User结构体中的CreditCard属性改为CreditCardId并与CreditCard结构体中的ID属性做映射。故就不做更多介绍了(实际使用还是很简单的)。
//代码1.5(来自官网)
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
type User struct {
gorm.Model
Name string
CreditCard CreditCard
}
2.查询数据操作(R)
在此次项目中,查询语句用到的最多的如下形式(代码2.1)。在下述代码中,会自动在result类型对应的数据表中去查询, 根据Where中的条件,将查询到的结果放在result中。如果要针对所有字段都有对应的条件进行查询,可以直接在Where中加入相应的结构体就行,如代码2.2。注意这里会有一个问题,代码2.2的形式并不会将字段为零值的条件加入(在这里我掉过大坑),因此,我更倾向于代码2.1的形式,不是很复杂还能灵活选择字段。
//代码2.1
var result User
db.Where("user_id = ?", user.id).Find(&result)
//代码2.2
db.Where(&user).Find(&result)
当仅需要返回表中的某些字段,可以通过Select筛选或者通过Obmit忽略。在项目中使用查询语句的时候,还接触到了db.Model,db.Model也可以起到指定到具体的表中进行查询。在项目中我使用到了查询某一个作品的点赞数量,当时设计了两种写法并且都进行了测试(代码2.3).
//代码2.3,第一种
var video Video
db.Where("id = ?", v.id).Find(&video)
return v.cnt
//代码2.3,第二种
var cnt uint
db.Model(&Video{}).Where("id = ?", id).Find(&cnt)
return cnt
了解了基本的查询语句后,剩下的一些高级查询,比如使用关键字in, between...and, not等都是类似的,只需要在Where中加入即可。同时,gorm特提供了group by, distinct 以及 joins等关键子,命名以及用法完全和sql语句相同,这里也不在阐述。
对于joins,在实际项目实践中用到的似乎也不多。 更多的是将查询拆分为多个子查询,根据这些子查询的结果再做一次查询。在本次项目中,有设计到用户表,视频表已经点赞关系表的三者联查,这里我也是用该方法处理的。
3.修改数据操作(U)
对于修改数据操作,此次项目中频繁使用到的是如下的修改操作(代码3.1)。!!!当更新多个列的时候,要注意updates是不会更新零值的,也就是说如果要对数据表中某个字段进行减一操作,当该字段减为0的时候,并不会刷新到数据库(写项目的时候栽了大坑),根据官方文档,可以使用map传参或者使用Select指定列。在这里我比较喜欢使用后者。我认为在代码的编写中,保持固定的风格是比较重要的,就比如前面的两个操作都倾向与使用结构体进行操作,那这里用结构体就行了,况且Select指定列的时候也没有很复杂。
//代码3.1
//更新单独列
db.Model(&Video{}).Where("id = ?", id).Update("action",2)
//更新多列
db.Model(&Video{}).Where("id = ?", id).Updates(video)
另外,在进行Update或者Updates的时候,Model中传入的参数除了指定操作的数据表之外,如果传入的参数中主键不为零,那么查询过程中会自动加入主键的匹配条件。
我认为gorm中更新操作最需要注意的是,全局更新是默认被禁止的。 因此在执行更新的时候需要指定条件。
4.删除数据操作(D)
在此次项目实践中,我认为删除最需要关注的一点就是软删除,如果结构体中包含deleted_at字段,在进行删除操作的时候,只会会此字段附上当前的实践并不会真正的删除,并且在查询的时候也会自动屏蔽deleted_at不等于null的记录(当然,如果没有此字段的话,删除就是真正意义上的删除了)。我使用较为频繁的基本删除操作如代码4.1。
//代码4.1
db.Where("age = ?", 20).Delete(&User{})
5.总结
gorm基本的crud还是很方便并且很容易上手的,通过项目的实践,我也更加熟悉了gorm基本的crud操作(写好crud可不是一件简单事)。当然,上述介绍到的crud操作只是整个框架中皮毛的皮毛。我只是列举了我使用非常广泛的几种操作并且记录了遇到的几次坑。希望能在后面的实践中学到更多。