GORM 字段使用自定义类型

文章目录

  • 起步
  • 方法1:类型别名
    • 场景 1
    • 场景 2
  • 方法2:定义结构体
    • 场景 3
  • 结合源码分析
    • Scan 与 Value 方法从何而来?
    • Valuer 接口的注意事项

起步

想在使用 GORM 时使用自定义类型必然事出有因,一般可有以下两种方式:

  • 方法 1:
type MyString string
  • 方法 2:
type MyString struct {
   
    string
}

当需求比较简单时,可采取方法1,也就是类型别名;如果需求复杂,就不得不把数据字段嵌入自定义结构体中。字段是否匿名并不重要,主要是用来承载目的数据。

单单把数据类型定义了还不够,还需要实现两个方法,你把这理解为一种协议即可。

// 写入数据库之前,对数据做类型转换
func (s MyString) Value() (driver.Value, error) {
   
    ...
}

// 将数据库中取出的数据,赋值给目标类型
func (s *MyString) Scan(v interface{
   }) error {
   
    ...
}

下面将结合我在实际开发遇到的业务场景,讲解为什么需要自定义类型,以及如何去实现上述的两个方法。

方法1:类型别名

场景 1

第一个场景:我需要自定义时间的显示格式。

当我的 model 嵌入 gorm.Model 时,会多四个字段,分别是:id, created_at, updated_at, deleted_at。

type Plan struct {
   
    gorm.Model
    Name string `gorm:"column:name"`
}

我面对的需求是,把数据从数据库中取出来,并按照规定的格式显示时间,最后返回给前端(需要 JSON 处理)。当然,我比较懒,希望直接取出数据,立马返给前端,时间的格式还是我期望的那样。为简便起见,这里只用到 created_at,name 两个字段。

先定义一个返给前端的数据结构:

type MyTime time.Time

// 返回给前端的数据结构
type Resp struct {
   
    CreatedAt MyTime `gorm:"column:created_at"`
    Name      string `gorm:"column:name"`
}

查询数据库代码如下。同时我用 json.Marshal 方法将结构体转换成 json 字符串,相当于模拟了将数据传递给前端的一个过程。

var resp Resp
db.Model(&Plan{
   }).Select("created_at, name").Limit(1).Scan(&resp)

data, _ := json.Marshal(resp)
log.Println(string(data))

然而日志输出不是我们想看到的:2020/02/16 19:21:28 {"CreatedAt":{},"Name":"早饭"}

这里还需要注意程序并没有报错。没报错是因为 MyTime 是 time.Time 类型的别名,两个类型之间允许相互转换。但是为什么输出是一个空值呢?

MyTime 作为 time.Time 的别名,但是并没有继承 time.Time 的方法,也就不支持 json.Marshal 转换。所以还需要为 MyTime 绑定 MarshalJSON 方法。

func (t MyTime) MarshalJSON() ([]byte, error) {
   
	tTime := time.Time(t)
	tStr := tTime.Format("2006/01/02 15:04:05") // 设置格式
	// 注意 json 字符串风格要求
	return [

你可能感兴趣的:(Go,gorm,custom,field,golang)