由于在大项目中我们采用了GORM,因此我直接总结在大项目中数据库使用的相关操作
mysql数据库创建
首先,我们需要先对mysql进行配置,创建两个数据库分别用于user和video两个微服务的数据库。
# 该数据库存放用户信息
User:
Address: 127.0.0.1
Port: 3306
Username: root
Password: "520666"
Dbname: tinytiktok-user
# 该数据库存放视频信息
Video:
Address: 127.0.0.1
Port: 3306
Username: root
Password: "520666"
Dbname: tinytiktok-video
同时我们需要在mysql中创建对应的表,user服务中包含有message、relation、user三个表,而video服务中则包含comment、detail、like、video四个表。但是我们还没有将它和golang结合在一起,我们的抖音demo还不能从数据库读写数据。需要使用gorm来读写数据,我们还需要先给每个表定义一个模型。
模型定义
Message:
type Message struct {
gorm.Model
Id int64 `json:"id" gorm:"user_id"`
UserId int64 `json:"user_id" gorm:"user_id"`
ReceiverId int64 `json:"receiver_id" gorm:"receiver_id"`
ActionType int64 `json:"action_type" gorm:"action_type"`
MsgContent string `json:"msg_content" gorm:"msg_content"`
}
Relation:
type Relation struct {
gorm.Model
ID int64 `gorm:"column:id" json:"id"`
UserID int64 `gorm:"column:userid" json:"userid"` // 自己
PID int64 `gorm:"column:pid" json:"pid"` // 好友
State bool `gorm:"column:state" json:"state"` // 状态 bool->uid关注pid false uid没关注pid
}
User:
type User struct {
gorm.Model
ID int64 `gorm:"column:id" json:"id"`
Name string `gorm:"column:name;unique" json:"name"`
Password string `gorm:"column:password" json:"-"`
Salt string `gorm:"column:salt" json:"-"`
FollowCount int64 `gorm:"column:follow_count" json:"follow_count"`
FollowerCount int64 `gorm:"column:follower_count" json:"follower_count"`
IsFollow bool `gorm:"column:is_follow" json:"is_follow"`
Avatar string `gorm:"column:avatar" json:"avatar"`
BackgroundImg string `gorm:"column:background_image" json:"background_image"`
Signature string `gorm:"column:signature" json:"signature"`
WorkCount int64 `gorm:"-" json:"work_count"`
FavoriteCount int64 `gorm:"-" json:"favorite_count"`
TotalFavorited int64 `gorm:"-" json:"total_favorited"`
}
Comment:
type Comment struct {
gorm.Model
ID int64 `gorm:"primaryKey" json:"id"`
UserID int64 `gorm:"column:user_id" json:"user_id"`
VideoID int64 `gorm:"column:video_id" json:"video_id"`
Content string `gorm:"column:content" json:"content"`
}
Detail:
type Detail struct {
gorm.Model
ID int64 `gorm:"column:id" json:"id"`
// 用户的作品数量
WorkCount int64 `gorm:"column:work_count" json:"work_count"`
// 用户喜欢的数量
FavoriteCount int64 `gorm:"column:favorite_count" json:"favorite_count"`
// 用户所有作品被点赞数量的总和 获赞数量
TotalFavorited int64 `gorm:"column:total_favorited" json:"total_favorited"`
}
Like:
type Like struct {
gorm.Model
ID int64 `gorm:"primaryKey" json:"id"`
UserID int64 `gorm:"column:user_id" json:"user_id"`
VideoID int64 `gorm:"column:video_id" json:"video_id"`
State bool `gorm:"column:state" json:"state"`
// 用于redis同步数据库
Table string `gorm:"-"`
IsEdit bool `gorm:"-"`
}
Video:
type Video struct {
gorm.Model
ID int64 `gorm:"primaryKey" json:"id"`
AuthorID int64 `gorm:"column:author_id" json:"author_id"`
PlayURL string `gorm:"column:play_url" json:"play_url"`
CoverURL string `gorm:"column:cover_url" json:"cover_url"`
FavoriteCount int64 `gorm:"column:favorite_count" json:"favorite_count"`
CommentCount int64 `gorm:"column:comment_count" json:"comment_count"`
Title string `gorm:"column:title" json:"title"`
}
将上述模型定义完了之后,我们接下来就可以对数据进行操作了。
数据库连接
接MySQL数据库,需要引入 gorm 和mysql两个包:
import ( "gorm.io/driver/mysql" "gorm.io/gorm" )
借助上述mysql配置建立起与mysql的连接:
dbConfig := config.NewConfig(fmt.Sprintf("%s/config", path), "mysql.yaml", "yaml")
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", username, password, address, port, dbName)
var err error
VideoDb, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
为了让mysql可以更好的工作,我们还需要再设置一下给连接对象设置空闲时的最大连接数、设置与数据库的最大打开连接数,每一个连接的生命周期等信息,具体配置如下:
sqlDB, err := db.DB()
if err != nil {
return err
}
sqlDB.SetMaxIdleConns(1000)//设置空闲时的最大连接数
sqlDB.SetMaxOpenConns(100000)//设置与数据库的最大打开连接数
sqlDB.SetConnMaxLifetime(-1)//每一个连接的生命周期等信息
自动迁移表
gorm还有一个强大的功能,就是自动迁移表功能。启用自动迁移模式可以保持mysql表更新到最新。
上一节我们已经创建好了7个表的模型,并且提到了可以使用 AutoMigrate 函数来实现自动迁移,现在我们将它们添加为自动迁移模式。
func Migrate() {
err := VideoDb.AutoMigrate(&models.Video{})
if err != nil {
panic(msg.UnableCreateTable)
}
err = VideoDb.AutoMigrate(&models.Like{})
if err != nil {
panic(msg.UnableCreateTable)
}
err = VideoDb.AutoMigrate(&models.Detail{})
if err != nil {
panic(msg.UnableCreateTable)
}
err = VideoDb.AutoMigrate(&models.Comment{})
if err != nil {
panic(msg.UnableCreateTable)
}
}
CRUD
接下来我们可以借助gorm来实现对数据的增删改查,这里由我实现的评论部分举例。
增:
首先要实现评论功能,我在发送评论时要将用户的评论存入数据库,具体代码如下:
// CommentVideo 将评论插入数据库
func CommentVideo(db *gorm.DB, comment *Comment) (int64, error) {
result := db.Create(comment)
if result.Error != nil {
return -1, result.Error
}
var CommentId int64
db.Raw("select LAST_INSERT_ID() as id").Pluck("id", &CommentId)
return CommentId, nil
}
注意,在我们插入评论时,我们需要在评论列表中实时返回我们插入的评论,因此我们在返回体中加入评论的内容(由id获取),我们知道gorm中的id是自增的,在插入到数据表中才能获取,因此我们在“增”之后,利用“select LAST_INSERT_ID()”将最新的id取出,方便后续在返回体中获取comment的具体内容。
删:
func DeleteComment(db *gorm.DB, commentID int64) error {
comment := Comment{}时时
db.Where("id = ?", commentID).Take(&comment)
result := db.Delete(&comment)
if result.Error != nil {
return result.Error
}
return nil
}
对于评论的删除,要注意的是,当模型的中存在deletedAt字段时,我们采用的是软删除,gorm只是将被删除的那行数据的deletedAt字段进行修改,该数据并没有被真正的删除。我们可以通过unscoped查询被软删除的数据
db.Unscoped().Where( "age = 20" ).Find(&users)
同样,我们也可以借助unscoped进行硬删除(永久删除):
db.Unscoped().Delete(&order)
查
我们可以借助视频的ID来获取其下面的所有评论,我们在初次打开视频时会需要用到这个操作。
// 通过VideoID获取评论列表
func GetCommentListByVideoID(db *gorm.DB, videoID int64) ([]*Comment, error) {
var comments []*Comment
err := db.Where("video_id = ?", videoID).Find(&comments).Error
if err != nil {
return nil, err
}
return comments, nil
}
同样,我们也可以借助用户id查询到该用户发布的所有评论,以及借助评论的id查询到评论内容。
在实际的设计当中,除了增删改查之外,我们还会将这些操作组合起来,实现更多的内容,例如借助视频的id来统计视频的评论数量,并在评论和删除评论时对评论数量进行改动:
func CalcCommentCountByVideoID(db *gorm.DB, videoID int64, isCommented bool) error {
var video Video
result := db.First(&video, videoID)
if result.Error != nil {
return result.Error
}
if isCommented {
video.CommentCount++
} else {
video.CommentCount--
}
result = db.Save(&video)
if result.Error != nil {
db.Rollback()
return result.Error
}
return nil
}