文章目录
- 自定义时间类型
- 自定义字符串数组
- 测试与完整代码
- 测试代码
- 测试结果
GORM 是GO语言中一款强大友好的ORM框架,但在使用过程中内置的数据类型不能满足以下两个需求,如下:
time.Time
类型返回的是2023-10-03T09:12:08.53528+08:00
这种字符串格式,需要额外处理,我们更希望默认的是是2023-10-03 09:12:08
这种可读性更高的格式- 有些数据字段需要存储数组形式,如下
Article
中Tags
字段希望保存不确定个字符串。直接保存会提示[error] unsupported data type: &[]
官方提供了
Scanner
和Valuer
两个接口,来满足自定义数据的存储、提取,本文记录上述两种结构解决方法。
type Article struct {Tags []string
}
自定义时间类型
自定义时间类型需满足以下两个需求:
- 返回
2006-01-02 15:04:05
格式CreatedAt
和UpdatedAt
使用时能按GORM
规范自动填充当前时间
默认的
time.Time
是能被gorm
自动存储,取出到结构体的,返回2023-10-03T09:12:08.53528+08:00
格式原因是在于json
序列化时,这里解决方案是自定义一个数据结构,添加JSON Marshal接口
,但是自定义的数据类型gorm
不能识别,所以要额外添加gorm
的Scanner
和Valuer
两个接口
type CustomTime time.Time// GORM Scanner 接口, 从数据库读取到类型
func (t *CustomTime) Scan(value any) error {if v, ok := value.(time.Time); !ok {return errors.Errorf("failed to unmarshal CustomTime value: %v", value)} else {*t = CustomTime(v)return nil}
}// GORM Valuer 接口, 保存到数据库
func (t CustomTime) Value() (driver.Value, error) {if time.Time(t).IsZero() {return nil, nil}return time.Time(t), nil
}// JSON Marshal接口,CustomTime结构体转换为json字符串
func (t *CustomTime) MarshalJSON() ([]byte, error) {t2 := time.Time(*t)return []byte(fmt.Sprintf(`"%v"`, t2.Format("2006-01-02 15:04:05"))), nil
}
自定义字符串数组
代码比较简单,直接定义一个类型实现
Scanner
和Valuer
两个接口,使用中将列定义为Strings
类型即可
type Strings []stringfunc (s *Strings) Scan(value any) error {v, _ := value.(string)return json.Unmarshal([]byte(v), s)
}
func (s Strings) Value() (driver.Value, error) {b, err := json.Marshal(s)return string(b), err
}
测试与完整代码
测试代码
package mainimport ("database/sql/driver""encoding/json""fmt""time""github.com/pkg/errors""github.com/glebarez/sqlite""gorm.io/gorm"
)type Strings []stringfunc (s *Strings) Scan(value any) error {v, _ := value.(string)return json.Unmarshal([]byte(v), s)
}
func (s Strings) Value() (driver.Value, error) {b, err := json.Marshal(s)return string(b), err
}type CustomTime time.Time// GORM Scanner 接口, 从数据库读取到类型
func (t *CustomTime) Scan(value any) error {if v, ok := value.(time.Time); !ok {return errors.Errorf("failed to unmarshal CustomTime value: %v", value)} else {*t = CustomTime(v)return nil}
}// GORM Valuer 接口, 保存到数据库
func (t CustomTime) Value() (driver.Value, error) {if time.Time(t).IsZero() {return nil, nil}return time.Time(t), nil
}// JSON Marshal接口,CustomTime结构体转换为json字符串
func (t *CustomTime) MarshalJSON() ([]byte, error) {t2 := time.Time(*t)return []byte(fmt.Sprintf(`"%v"`, t2.Format("2006-01-02 15:04:05"))), nil
}// fmt.Printf, 【可选方法】
func (t CustomTime) String() string {return time.Time(t).Format("2006-01-02 15:04:05")
}type Article struct {ID uint `gorm:"primaryKey"`Tags StringsCreatedAt CustomTimeUpdatedAt CustomTime
}func main() {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}db.AutoMigrate(&Article{})db.Create(&Article{Tags: []string{"go", "java"}})var article Articledb.Last(&article)fmt.Printf("article is: %v\n", article)b, _ := json.Marshal(&article)fmt.Printf("article json is: %s\n", string(b))time.Sleep(time.Second * 30)article.Tags = append(article.Tags, "python")db.Save(&article)db.Last(&article)fmt.Printf("updated article is: %v\n", article)b, _ = json.Marshal(&article)fmt.Printf("updated article json is: %s\n", string(b))
}
测试结果
字符串数组
自定义时间,可以看到满足
2006-01-02 15:04:05
格式输出,以及时间自动添加和更新