一、gorm简介
golang使用数据库服务一般要到标准库的database/sql 和 database/sqldrivers,标准库的使用一般涉及到写sql语句、Query可以查询多行、QueryRow查询特定的行、Exec 执行更新插入删除操作等。使用传统的方法需要先在database中创建好表,而gorm可以通过定义Model的形式很方便的创建或修改数据库表的结构。 除了封装以外,gorm添加了自己独特的特性:
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持
Preload、Joins的预加载 - 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
本文通过实践介绍gorm的基本使用方法
二、安装gorm
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
三、定义表结构
使用gorm实现一个简单的场景:某主播喜欢通过vlog记录自己的生活,他会上传自己的视频,而其他用户可以看到该视频并且可以给他点赞。他希望使用数据库查询自己发布了哪些视频,并且自己的总点赞数是多少。
定义:
type Video struct {
ID int64 `json:"id"`
AuthorID int64 `json:"author_id"`
PublishTime time.Time `json:"publish_time"`
Title string `json:"title"`
}
// 表示某用户给某个视频点赞
type Favorites struct {
ID int64 `json:"id"`
UserId int64 `json:"user_id"`
VideoId int64 `json:"video_id"`
}
四、连接数据库并创建表
// 打开数据库连接
dbConn, err = gorm.Open(mysql.Open(MySQLDSN), &gorm.Config{
PrepareStmt: true,
SkipDefaultTransaction: true,
})
if err != nil {
panic(err)
}
// 创建数据库表
if !dbConn.Migrator().HasTable(&Favorites{}) {
err = dbConn.Migrator().CreateTable(&Favorites{})
if err != nil {
panic(err)
}
}
if !DB.Migrator().HasTable(&Video{}) {
err = DB.Migrator().CreateTable(&Video{})
if err != nil {
panic(err)
}
}
五、 CRUD
添加数据
此时我们可以适当向Video表和Favorite表添加几条信息, 表示用户1 发布了两条视频(id分别为1 和 2), 用户2 发布视频(id为3)。用户1 视频3点赞,用户2给视频1和2点赞。
videos := []*Video{
{AuthorID: 1, PublishTime: time.Now().Unix(), Title: "test1"},
{AuthorID: 1, PublishTime: time.Now().Unix(), Title: "test2"},
{AuthorID: 2, PublishTime: time.Now().Unix(), Title: "test3"},
}
favorites := []*Favorite{
{UserId: 1, VideoId: 3},
{UserId: 2, VideoId: 1},
{UserId: 2, VideoId: 2},
}
查询数据
查询id为2的视频 查询单个对象:
var video Video
方法一:
err := DB.First(&video, 2).Error
// 注意该方法在找不到记录时会返回gorm.ErrRecordNotFound, 因此我们要先检查该类型的错误
if err == gorm.ErrRecordNotFound {
// 找不到记录时的逻辑
}
if err != nil {...}
方法二:
err := DB.Limit(1).Find(&video, 2).Error
if err != nil {...}
查询多个对象:
var videos []*Video
err := DB.Where("AuthorId = ?", 1).Find(&videos).Error
if err != nil {...}
多表查询:
子查询方式:
err := dbConn.WithContext(ctx).Where("video_id in (?)",
dbConn.WithContext(ctx).Table(constants.VideoTableName).Where("author_id = ?", authorId).Select("video_id")).Find(&favorites).Error
if err != nil {...}
join方式:
err := dbConn.WithContext(ctx).Table(constants.FavoriteTableName).
Joins("JOIN videos ON favorites.video_id = videos.id").
Where("videos.author_id = ?", authorId).Count(&sum).Error
if err != nil {...}
删除数据
var video Video
err := dbConn.Where("AuthorId = ?", 1).Delete(&video)
更新数据
方法一:
video := Video{ID: 1, AuthorID: 4, PublishTime: time.Now().Unix(), Title: "test4"}
DB.Model(&Video{}).Updates(video)
// 该方式下不会更新零值字段
方法二:
DB.Model(&video).Updates(map[string]interface{}{"AuthorId": 3}
结语
官网地址:GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.