伴学笔记创作活动的第 10 天 发现课程权限开放了,加上最近也在积极准备找实习的事情,因此把自己简历里面写的知识点好好过一遍。
// 使用 Find 方法
var users []User
result := db.Where("name = ?", "John").Find(&users)
// result.Error 可以用于检查是否出错
// users 数组将包含所有名为 John 的用户记录
// 使用 First 方法
var user User
result := db.Where("name = ?", "John").First(&user)
// result.Error 可以用于检查是否出错
// user 将包含第一个名为 John 的用户记录
下面是青训营大项目代码部分用到的gorm,代码部分给出了很详细的解释。
// 定义一个gorm model
type CommentRaw struct {
gorm.Model
UserId int64 `gorm:"column:user_id;not null;index:idx_userid"`
VideoId int64 `gorm:"column:video_id;not null;index:idx_videoid"`
Content string `gorm:"column:content;type:varchar(255);not null"`
}
// 为model 定义表名
func (CommentRaw) TableName() string {
return constants.CommentTableName
}
// CreateComment 通过一条评论创建一条评论记录并增加视频评论数
/*
DB是一个GORM的*gorm.DB实例,表示一个数据库连接。
WithContext(ctx)是将一个context.Context对象和数据库连接绑定,以实现在数据库操作中使用context.Context上下文传递。
Table(constants.CommentTableName)表示要对名为constants.CommentTableName的数据表进行操作。
Create(&comment)表示在该表中创建一条记录,其中comment是一个被传入的参数,表示要插入到数据库中的数据。
&comment表示将comment参数取地址,使其变成指向comment的指针,这样Create()方法就可以直接通过指针修改comment的值,并将其写入数据库。
如果执行成功,则err的值为nil;如果失败,则err将包含一个error类型的值,表示发生了什么错误。
*/
func CreateComment(ctx context.Context, comment *CommentRaw) error {
err := DB.WithContext(ctx).Table(constants.CommentTableName).Create(&comment).Error
if err != nil {
klog.Error("create comment fail " + err.Error())
return err
}
return nil
}
// DeleteComment 通过评论id号删除一条评论并减少视频评论数,返回该评论
/*
首先,函数会通过DB.WithContext(ctx)绑定上下文和数据库连接,然后使用gorm库的Where()方法查询指定ID的评论记录,并将查询结果存储到commentRaw指针变量中。
如果查询结果为空,则会返回一个gorm.ErrRecordNotFound类型的错误;如果查询出错,则会返回一个error类型的错误。
接下来,如果查询成功,则会调用DB.WithContext(ctx)和Delete()方法,从数据库中删除指定ID的评论记录。
如果删除成功,则会返回之前存储的commentRaw指针变量,否则会返回一个error类型的错误。
如果查询失败或删除失败,则会记录错误信息,并返回一个error类型的错误。
需要注意的是,删除记录时的操作对象是一个空的CommentRaw{},而不是存储查询结果的commentRaw指针变量。
这是因为GORM使用该类型来生成SQL语句,以执行实际的删除操作。
*/
func DeleteComment(ctx context.Context, commentId int64) (*CommentRaw, error) {
var commentRaw *CommentRaw
err := DB.WithContext(ctx).Where("id = ?", commentId).First(&commentRaw).Error
if err == gorm.ErrRecordNotFound {
klog.Errorf("not find comment %v, %v", commentRaw, err.Error())
return nil, err
}
if err != nil {
klog.Errorf("find comment %v fail, %v", commentRaw, err.Error())
return nil, err
}
err = DB.WithContext(ctx).Where("id = ?", commentId).Delete(&CommentRaw{}).Error
if err != nil {
klog.Error("delete comment fail " + err.Error())
return nil, err
}
return commentRaw, nil
}
// QueryCommentByCommentIds 通过评论id查询一组评论信息
/*
这里使用的是find,用来查询多条评论信息。上一个函数我们查的是单条信息,用的是first。注意区别
*/
func QueryCommentByCommentIds(ctx context.Context, commentIds []int64) ([]*CommentRaw, error) {
var comments []*CommentRaw
err := DB.WithContext(ctx).Table(constants.CommentTableName).Where("id in (?)", commentIds).Find(&comments).Error
if err != nil {
klog.Error("query comment by comment id fail " + err.Error())
return nil, err
}
return comments, nil
}
提取知识点:
1.context.Context
在一个应用程序中,通常会在HTTP请求处理程序中创建一个Context对象,然后将其传递给需要执行的Goroutine,以确保它们都能够共享相同的Context,这有助于确保正确性、一致性和可维护性。另外,Context也可以用于控制与外部服务的交互,例如将其用于数据库连接、RPC调用等操作。
2.DB.WithContext(ctx)
在进行数据库操作时,可以使用DB.WithContext(ctx)方法将上下文与数据库操作关联起来,例如:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
db.WithContext(ctx).Find(&users)
在上面的示例中,我们使用context.WithTimeout()方法创建了一个带有超时时间的上下文,并将其与数据库操作关联起来。然后,我们使用DB.WithContext(ctx)方法返回一个新的与上下文关联的*gorm.DB对象,以确保数据库操作能够在超时时间内完成。最后,我们在新的*gorm.DB对象上调用Find()方法来执行数据库查询。
通过使用DB.WithContext(ctx)方法,我们可以确保数据库操作能够与上下文关联起来,并响应上下文中的取消、超时等事件,从而使我们的应用程序更加健壮和可靠。
3.DB.WithContext(ctx).Table()
DB.WithContext(ctx).Table()是GORM中的一个方法,它与Table()方法的作用相同,但它还将上下文(Context)与数据库操作关联起来,以便在数据库操作的过程中可以进行取消、超时等相关操作。
在GORM中,Table()方法用于设置要查询的表名。通常情况下,我们会在DB对象上直接调用Table()方法,如下所示:
db.Table("users").Where("name = ?", "John").Find(&users)
但是,在进行大量的复杂查询时,我们可能需要将上下文与查询操作相关联,以便能够响应取消、超时等事件。
一、Gorm事务
在 GORM 中,使用 Begin() 方法可以开启一个事务。事务可以将一组数据库操作视为单个操作,要么全部成功,要么全部失败,可以保证数据库数据的一致性和完整性。如果事务中任何一个操作失败,整个事务都会被回滚,之前的操作也会被撤销。
在事务中,需要执行的操作应该使用 Tx 对象来执行,而不是直接使用 DB 对象。如果事务全部成功执行完成,需要使用 Commit() 方法提交事务;如果事务执行失败,则需要使用 Rollback() 方法回滚事务。
下面是一个使用 GORM 开启事务的示例:
tx := db.Begin() // 开启事务
if tx.Error != nil {
// 错误处理
}
// 在事务中执行数据库操作
if err := tx.Create(&User{Name: "Tom"}).Error; err != nil {
tx.Rollback()
// 错误处理
}
if err := tx.Create(&Order{UserID: 1, Amount: 100}).Error; err != nil {
tx.Rollback()
// 错误处理
}
// 如果所有操作都成功,提交事务
if err := tx.Commit().Error; err != nil {
tx.Rollback()
// 错误处理
}
在上面的示例中,我们首先使用 Begin() 方法开启一个事务,并检查是否有错误发生。然后,在事务中执行了两个数据库操作,如果其中一个操作失败,则使用 Rollback() 方法回滚事务。如果所有操作都成功,则使用 Commit() 方法提交事务。
需要注意的是,在 GORM 中使用事务时,必须要使用相同的 *gorm.DB 对象来开启事务和执行数据库操作。这是因为事务需要绑定在同一个数据库连接上。因此,不应该在事务中再次创建一个新的 *gorm.DB 对象。
二、Transaction
GORM 中的 Transaction() 方法是用来开启事务的,与 Begin() 方法不同的是,它可以将多个数据库操作绑定在一个事务中,使得整个事务要么全部成功,要么全部失败。
在事务中,需要执行的操作应该使用 tx 对象来执行,而不是直接使用 db 对象。如果事务全部成功执行完成,需要使用 Commit() 方法提交事务;如果事务执行失败,则需要使用 Rollback() 方法回滚事务。
下面是一个使用 GORM 的 Transaction() 方法开启事务的示例:
err := db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&User{Name: "Tom"}).Error; err != nil {
return err
}
if err := tx.Create(&Order{UserID: 1, Amount: 100}).Error; err != nil {
return err
}
return nil
})
if err != nil {
// 错误处理
}
在上面的示例中,我们使用 Transaction() 方法开启一个事务,并在事务中执行了两个数据库操作。如果其中一个操作失败,则事务会自动回滚,否则事务会自动提交。
需要注意的是,在 GORM 中使用 Transaction() 方法时,必须要使用相同的 *gorm.DB 对象来执行数据库操作。这是因为事务需要绑定在同一个数据库连接上。因此,不应该在事务中再次创建一个新的 *gorm.DB 对象。
与 Begin() 方法相比,使用 Transaction() 方法开启事务更加简洁方便,也更加容易管理。
三、Gorm Hook
GORM 中的 Hook 是在执行数据库操作时触发的回调函数,可以在执行数据库操作前或操作后进行一些处理。
GORM 提供了以下几种 Hook:
BeforeSave:在将对象保存到数据库之前触发。AfterSave:在将对象保存到数据库后触发。BeforeUpdate:在将对象更新到数据库之前触发。AfterUpdate:在将对象更新到数据库后触发。BeforeDelete:在将对象从数据库中删除之前触发。AfterDelete:在将对象从数据库中删除后触发。BeforeFind:在查询数据库之前触发。AfterFind:在从数据库中查询出对象后触发。
每个 Hook 都有一个对应的方法,在 GORM 模型结构体中实现即可。以 BeforeSave Hook 为例,实现方法的签名为:
func (u *User) BeforeSave(tx *gorm.DB) (err error)
其中,tx 参数是当前的 *gorm.DB 对象,可以在 Hook 中对它进行一些操作。
下面是一个使用 BeforeSave Hook 的示例:
type User struct {
gorm.Model
Name string
Age int
}
func (u *User) BeforeSave(tx *gorm.DB) (err error) {
if u.Age <= 0 {
return errors.New("invalid age")
}
return nil
}
db.Create(&User{Name: "Tom", Age: 20}) // OK
db.Create(&User{Name: "Jerry", Age: -1}) // error: invalid age
在上面的示例中,我们在 User 模型中实现了 BeforeSave Hook,当保存一个年龄小于等于 0 的用户时,会返回一个错误。这样,我们就可以在 Hook 中进行一些数据验证或格式化操作,确保数据的正确性和完整性。
需要注意的是,Hook 可以在模型结构体中实现,也可以在全局范围内实现,对于某些操作可能更加方便。例如,如果需要对所有模型都执行某个操作,可以在全局范围内实现对应的 Hook。具体实现方法可以参考 GORM 的官方文档。