为什么用Gen生成代码
字段类型校验,过滤参数错误,为数字、字符串、布尔类型、时间类型硬编码制定差异化类型安全的表达式方法,杜绝了 SQL 注入的风险,能跑就安全;
映射数据库表像,DB 里面有数据表就能生成对应的 Golang 结构体;
用注释的形式描述查询的逻辑后,一键即可生成对应的安全可靠查询API。
作者:ag9920 链接:juejin.cn/post/713315… 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本文用到的工具大概关系如下图
步骤
安装
go get -u gorm.io/gen
创建数据库模型
新建一个go文件在model下,例如
model/publish.go就是数据库中publish表的数据,我们后面要对它进行PO
// Publish 视频数据
type Publish struct {
gorm.Model
UserId int64 `json:"user_id" gorm:"not null"`
Title string `json:"title" gorm:"varchar(20);not null"`
PlayUrl string `json:"play_url" gorm:"varchar(255);not null"`
CoverUrl string `json:"cover_url" gorm:"varchar(255);not null"`
}
Gorm 会自动生成Row_ID, created_at, deleted_at, changed_at
创建数据库表
根据上一步的模型使用 gorm 来创建 table, 依赖的是 Gorm 的 Migrate能力。这里添加你刚刚弄的struct:
err = db.AutoMigrate(&model.Publish{})
if err != nil {
panic("publish model has changed == > transfer to new model struct" + err.Error())
}
最后run一下这个go
// use gorm to migrate database
func main() {
db, err := gorm.Open(mysql.Open(config.AppConfig.DSN), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
panic("mysql - database connect error == > " + err.Error())
}
// migrate user model
err = db.AutoMigrate(&model.User{})
if err != nil {
panic("user model has changed == > transfer to new model struct" + err.Error())
}
// migrate publish model
err = db.AutoMigrate(&model.Publish{})
if err != nil {
panic("publish model has changed == > transfer to new model struct" + err.Error())
}
}
run这个文件,检查数据库是否新增了一个表
生成代码文件
运行cmd/gorm-gen目录下的gen.go,这里是调用gorm/gen的动态生成能力实现数据库操作的代码生成
其中dynamic SQL的约定可以参考官网gorm.io/gen/dynamic…
type Querier interface {
// GetByRoles query data by roles and return it as *slice of pointer*
// (The below blank line is required to comment for the generated method)
// //
// SELECT * FROM @@table WHERE role IN @rolesName
GetByRoles(rolesName ...string) ([]*gen.T, error)
}
func generateModelByGen() {
gormgen := gen.NewGenerator(gen.Config{
OutPath: "repo",
Mode: gen.WithDefaultQuery | gen.WithQueryInterface | gen.WithoutContext,
})
gormDB, err := gorm.Open(mysql.Open(config.AppConfig.DSN))
fmt.Println("gormDB", config.AppConfig.DSN)
if err != nil {
panic("mysql - database connect error == > " + err.Error())
}
gormgen.UseDB(gormDB)
// generate basic DAO API for struct 'model.Publish'
gormgen.ApplyBasic(model.Publish{})
// generate type-safe API for struct 'model.Publish'
gormgen.ApplyInterface(func(Querier) {}, model.Publish{})
gormgen.Execute()
}
func main() {
generateModelByGen()
}
其中,Querier定义了一个动态SQL,代表的SQL语句也按照gorm/gen的约定描述在注释中,所以gen会按照这个生成对应的go操作函数。
指定我们要生成的代码要放到 repo 子包,生成模式暂时用 default 就ok。
我们通过
gen.NewGenerator来构造一个【代码生成器】,调用
ApplyBasic基于两个 model 来生成基础 DAL 代码;调用
ApplyInterface,指明我们希望基于什么 model 和 interface 来生成自定义的接口实现。最后调用
Execute方法来触发生成。作者:ag9920 链接:juejin.cn/post/713315… 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
运行main函数后,repo文件夹下应该有生成的函数。
使用
至此,我们就实现了使用gorm/gen实现数据库操作的代码生成。
然后就可以开始使用生成的代码对数据库进行CRUD啦!!
// q is the query struct gorm use to connect db
q := repo.Q
// connect to your model
publishIDo := q.Publish
// do it with/without context
publishIDo = q.WithContext(ctx).Publish
// then do gorm CRUD
newPublishModel := model.Publish{
UserId: 123456,
Title: req.Title,
PlayUrl: fileLink,
CoverUrl: coverLink,
}
fmt.Println("new publish model == > ", newPublishModel)
//repo.Q.New
err = publishIDo.Create(&newPublishModel)
在测试中
这里有一个小坑。在测试中gorm好像不会想在全局运行时一样初始化拿到数据库连接,需要自己设置一下:
func TestDouyinPublishActionServiceImpl_DouyinPublishAction(t *testing.T) {
// get video file in curent directory
testFile, err := os.ReadFile("./resource/test.mp4")
if err != nil {
t.Fatal(err)
}
repo.SetDefault(repo.DB)