环境配置
安装:
go get -u gorm.io/gorm
// mysql驱动
go get -u gorm.io/driver/musql
项目引入:
import (
"gorm.io/gorm"
"gorm.io/driver/mysql"
)
模型定义
模型是标准的struct,根据数据表设计,创建Video模型如下:
type Video struct {
ID int 64
AuthorID int64
PlayURL string
CoverURL string
Title string
}
约定
默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,若要关闭蛇形复数命名,则创建连接时,需在gorm.Config中配置:
db, err := gorm.Open(mysql.Open(datasourceConstants.DSNString()),
&gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
})
标签
GORM默认使用ID作为表的主键。若要修改主键为其他字段,可使用primaryKey标签声明;通过为多个字段设置primaryKey,可创建复合主键:
type Favorite struct {
VideoID int64 `gorm:"primaryKey"`
UserID int64 `gorm:"primaryKey"`
}
默认情况下,整型PrioritizedPrimaryField 启用了 AutoIncrement,要禁用它,需要为整型字段关闭 autoIncrement:
type Favorite struct {
VideoID int64 `gorm:"primaryKey;autoIncrement:false"`
UserID int64 `gorm:"primaryKey;autoIncrement:false"`
}
使用index标签创建索引,not null标签指定列为NOT NULL,更多字段标签见:模型定义 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
gorm.Model
GORM定义一个grom.Model结构体,可将其嵌入我们自己的模型中:
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
嵌入结构体,改进video模型如下:
type Video struct {
gorm.Model //包含ID、CreatedAt、UpdatedAt、DeletedAt
AuthorID int64 `gorm:"index"` //索引
PlayURL string
CoverURL string
Title string
}
连接MySQL
GORM支持连接的数据库类型有:MySQL, PostgreSQL, SQLite, SQL Server 和 TiDB,下面以MySQL为例,展示连接数据库,并设置GORM配置的方法:
var datasourceConstants = &constant.AllConstants.Datasource
func InitGorm() *gorm.DB {
db, err := gorm.Open(mysql.Open(datasourceConstants.DSNString()),
&gorm.Config{
PrepareStmt: true, \\开启PrepareStmt以提高效率
SkipDefaultTransaction: true, \\跳过默认事务
})
...
return db
}
上面代码中连接到数据库时初始化了GORM配置,其中:
SkipDefaultTransaction为跳过默认事务:为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除),没有这方面需求可设为true,将获得性能提升,反之为false;
PrepareStmt:开启后在执行任何 SQL 时都会创建一个 prepared statement 并将其缓存,以提高后续的效率;
CRUD
创建
创建记录:
先填充好要创建的记录的结构体,即上文定义的模型,再通过传递数据指针调用db.Create创建,返回的结果中携带插入数据的主键、error、插入记录条数。插入一条video记录:
package dal
var db *gorm.DB
type Video struct {
gorm.Model
AuthorID int64 `gorm:"index"`
PlayURL string
CoverURL string
Title string
}
func SetVideoDB() {
db = dal.InitGorm()
}
func CreateVideo(video *Video) (int64, error) {
result := db.Create(video)
return int64(video.ID), result.Error
}
package service
vid, err := dal.CreateVideo(&dal.Video{
AuthorID: id,
PlayURL: videoURL,
CoverURL: coverURL,
Title: req.Title,
})
创建记录时也可指定字段:分为为为指定字段分配值和为指定字段忽略值两种:
db.Select("PlayURL", "CoverURL").Create(&Video)
db.Omint("AuthorID", "Title").Create(&Video)
Create方法也可以创建多条记录
查询
查询单条记录:
GORM提供的First、Take、Last方法用于从数据库中检索单个对象,查询时添加了LIMIT 1,没有找到记录时会返回ErrRecordNotFound错误(可以使用db.Limit(1).Find(&video)来避免ErrRecordNotFound错误,但对单个对象使用db.Find(&video)不带limit,将会查询整个表且只返回第一个对象,该做法性能不高且返回结果不确定),其中:
db.First(&video)等价于:MYSQLSELECT * FROM videos ORDER BY id LIMIT 1;
db.Take(&video)等价于:MYSQLSELECT * FROM users LIMIT 1;
db.Last(&video)等价于:MYSQLdb.Last(&user)
若要根据主键检索,如根据视频id检索视频,则在上述方法中添加主键参数: (当主键为string类型时,使用该方法应小心SQL注入)
func GetVideo(vID int64) (*Video, error) {
var video Video
result := GormDB.First(&video, vID) //SELECT * FROM videos WHERE id = ;
return &video, result.Error
}
列表查询:
db.Find(&videos, []int{1, 2, 3})等价于:
SELECT * FROM users WHERE id IN (1,2,3);
根据视频id列表查询视频列表:
func GetVideosByIDs(vIDs []int64) ([]*Video, error) {
var videos []*Video
result := GormDB.Find(&videos, vIDs)
return videos, result.Error
}
条件查询:
db.Where("author_id = ?", 1).Find(&video)等价于:
SELECT * FROM videos WHERE author_id = 1;
(使用条件查询时,若传入的video中查询字段已经有值,条件不会覆盖,而是and合并)
根据视频作者id获取视频列表:
func GetVideosByAuthor(authorID int64) ([]*Video, error) {
var videos []*Video
result := GormDB.Where("author_id = ?", authorID).Find(videos)
return videos, result.Error
}
排序:
db.Order("created_at desc").Order("xxx").Find(&video)等价于:
SELECT * FROM video ORDER BY created_at desc, xxx;
按发布时间倒序排列视频列表:
func GetVideosByAuthor(authorID int64) ([]*Video, error) {
var videos []*Video
result := GormDB.Where("author_id = ?", authorID).Order("created_at desc").Find(videos)
return videos, result.Error
}
按时间倒序获取获取指定时间前发布的30条视频列表:
func GetVideosDescByTimeLimit30(latestTime int64) ([]*Video, error) {
var videos []*Video
videoTime := time.UnixMilli(latestTime).Format("2006-01-02 15:04:05")
result := GormDB.Where("created_at < ?", videoTime).Order("created_at desc").Limit(30).Find(&videos)
return videos, result.Error
}
更新
更新所有列:
db.Save(&video)方法会保存或更新video模型中的所有字段,即使字段是零值;当主键字段无值时,save会执行Create,否则执行Update。(Save方法不支持和Model连用)
单个列更新:
使用Update更新单个列时需要给定更新条件,否则会报ErrMissingWhereClause错误;当使用的Model方法且其具有主键值时,主键值将会被用于构造更新条件。
如video中id为1,
db.Model(&video).Where("author_id = ?", 2).Update("title", "newTitle")方法更新id为1且author_id为2的视频的title列值为newTitle
多列更新:
Updates方法支持struct与map[string]interface{},通过struct更新时,默认只更新其中的非零字段(可使用map或select来更新其它指定列)。
db.Model(&video).Updates(Video{author_id: 2, title: "newTitle"})
db.Model(&video).Updates(map[string]interface{}{"author_id": 2, "title": "newTitle"})
删除
GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,删除一条记录时,删除记录需要指定主键。如果指定的值不包括主键,那么 GORM 会执行批量删除,它将删除所有匹配的记录。当执行不带任何条件的批量删除时,GORM将不会运行并返回ErrMissingWhereClause 错误
包含
gorm.DeletedAt字段(该字段也被包含在gorm.Model中)的模型具有软删除的功能。调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。Unscoped可用来来查询到被软删除的记录:db.Unscoped().Find(&video)、也可用来永久删除记录:db.Unscoped().Delete(&video)
删除指定user_id和video_id的全部favorite记录:
type Favorite struct {
gorm.Model //软删除能力
VideoID int64 `json:"video_id"`
UserID int64 `json:"user_id"`
}
func DeleteFavorite(userID int64, videoID int64) error {
return db.Where("user_id = ? AND video_id = ?", userID, videoID).Delete(&Favorite{}).Error //删除所匹配记录
}