一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
近期观察到 gorm.io/gorm 新增了一个由 jinzhu 亲自操刀,很强大的feature:Serializer。可能由于时间较紧,gorm的官方文档暂时还没有更新,自己先来尝个鲜。
示例场景
先回顾一下以前我们是怎样定义 gorm model 的:
模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成
from gorm.io/zh_CN/docs/…
日常开发中,很多时候我会以 Go 基本数据类型为主来定义一个纯粹给 gorm 使用的 model,字段和数据表 schema 一一对应。比如我需要开发一个应用,业务上看需要将应用ID,配置存入数据库。
type Application struct {
gorm.Model
AppID int64 `gorm:"column:app_id" json:"app_id"`
Config string `gorm:"column:config" json:"config"`
}
这里的 Config 字段,对应到底层数据表是一个 text 类型,如下
`config` text COLLATE utf8mb4_general_ci COMMENT '配置项'
但实际上,config 本身是一个 json 序列化的字符串,包含了这个应用的相关配置信息。
type ApplicationConfig struct {
Name string
AvatarURL string
WebURL string
}
在实际存储数据时,我们会先把获取到的 ApplicationConfig 序列化为字符串,塞到 Application 的 Config 字段
package main
import (
"encoding/json"
"log"
"os"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
type Application struct {
gorm.Model
AppID int64 `gorm:"column:app_id" json:"app_id"`
Config string `gorm:"column:config" json:"config"`
}
type ApplicationConfig struct {
Name string
Desc string
AvatarURL string
WebURL string
}
func main() {
// initialize gorm
newLogger := logger.New(
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
logger.Config{
SlowThreshold: time.Second, // Slow SQL threshold
LogLevel: logger.Info, // Log level
IgnoreRecordNotFoundError: true, // Ignore ErrRecordNotFound error for logger
Colorful: true, // Disable color
},
)
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: newLogger,
})
if err != nil {
panic("failed to connect database")
}
// Migrate the schema
db.AutoMigrate(&Application{})
// Construct Biz Config
bizModel := &ApplicationConfig{
Name: "testName",
Desc: "testDesc",
AvatarURL: "https://image.url",
WebURL: "http://test.web.url",
}
val, _ := json.Marshal(bizModel)
// call gorm.DB.Create
db.Create(&Application{
AppID: 1,
Config: string(val),
})
}
执行demo后发现打印出的 INSERT 语句如下:
INSERT INTO `applications` (`created_at`,`updated_at`,`deleted_at`,`app_id`,`config`) VALUES ("2022-02-23 17:29:41.466","2022-02-23 17:29:41.466",NULL,1,"{\"Name\":\"testName\",\"Desc\":\"testDesc\",\"AvatarURL\":\"https://image.url\",\"WebURL\":\"http://test.web.url\"}") RETURNING `id`
使用 Serializer 简化
从上面的例子可以看到,我们要做到将一个 json 字符串插入数据库,需要提前自行序列化,再通过为 gorm 定义的 model 来完成 Create 操作。
有没有办法简化呢?有!Serializer 就是来帮你完成序列化相关操作的。
有了 Serializer,我们无需再将原有的序列化结构进行 json 序列化,再塞到 model中,而是直接在 model 中将需要序列化的字段加上 serializer tag。看下效果:
原来的 Config 字段定义:
Config string `gorm:"column:config" json:"config"`
改为
Config *ApplicationConfig `gorm:"column:config;serializer:json" json:"config"`
原来的 Create 操作:
bizModel := &ApplicationConfig{
Name: "testName",
Desc: "testDesc",
AvatarURL: "https://image.url",
WebURL: "http://test.web.url",
}
val, _ := json.Marshal(bizModel)
db.Create(&Application{
AppID: 1,
Config: string(val),
})
改为
db.Create(&Application{
AppID: 1,
Config: &ApplicationConfig{
Name: "testName",
Desc: "testDesc",
AvatarURL: "https://image.url",
WebURL: "http://test.web.url",
},
})
这次生成的 INSERT语句如下:
INSERT INTO `applications` (`created_at`,`updated_at`,`deleted_at`,`app_id`,`config`) VALUES ("2022-02-23 17:42:59.57","2022-02-23 17:42:59.57",NULL,1,"{\"Name\":\"testName\",\"Desc\":\"testDesc\",\"AvatarURL\":\"https://image.url\",\"WebURL\":\"http://test.web.url\"}") RETURNING `id`
gorm自动处理了json序列化的操作,生成的 INSERT语义跟原来的一模一样。从此再也不用拆分多个 model了,直接使用 serializer tag。
不仅仅是单独的 struct 嵌套,serializer 还支持对 slice, map 进行序列化,进行序列化的方法也不限制于json,官方目前提供了 json, unixtime, gob 的支持,同学们也可以自行提PR 支持更多序列化的方法。
gorm 对于 serializer tag 的单测:github.com/go-gorm/gor…