GORM 字段使用自定义类型

本文介绍了在 GORM 中使用自定义类型的方法,包括类型别名和定义结构体两种方式。针对时间格式化和类型限制的需求,详细阐述了自定义类型如何配合 Scan 和 Value 方法工作,以确保数据在数据库和 Go 代码之间正确转换。通过实例展示了在实际开发中自定义类型的重要性,并分析了 Valuer 接口的注意事项。
摘要由CSDN通过智能技术生成

起步

想在使用 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 []byte(fmt.Sprintf("\"%v\"", tStr)), nil
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值