自定义数据类型
在使用GORM进行Go语言的数据库操作时,有时候会遇到内置数据类型不能满足需求的情况。可以通过实现GORM的Scanner和Valuer接口来创建自定义数据类型,使其能够与数据库交互。
TIP
要在GORM中使用这些自定义类型,需要实现两个接口
Scanner
:用于从数据库读取值Valuer
:用于将值写入数据库
Scanner
和Valuer
接口定义如下,如果要使用自定义数据类型,需要实现Scan
和Value
方法
Go
type Scanner interface {
// Scan assigns a value from a database driver.
//
// The src value will be of one of the following types:
//
// int64
// float64
// bool
// []byte
// string
// time.Time
// nil - for NULL values
//
// An error should be returned if the value cannot be stored
// without loss of information.
//
// Reference types such as []byte are only valid until the next call to Scan
// and should not be retained. Their underlying memory is owned by the driver.
// If retention is necessary, copy their values before the next call to Scan.
Scan(src any) error
}
type Valuer interface {
// Value returns a driver Value.
// Value must not panic.
Value() (Value, error)
}
代码示例
准备数据和自动建表
Go
package main
type CUser struct {
Gender uint8
Info CInfo
}
type CInfo struct {
Name string
Age uint8
}
func cAutoMigrate() {
err := DB.AutoMigrate(&CUser{})
if err != nil {
return
}
}
func main() {
cAutoMigrate()
}
实现Valuer
接口,处理存入数据库中的值
Go
type CInfo struct {
Name string
Age uint8
}
// Value c就是要向数据库中存储的值
func (c CInfo) Value() (driver.Value, error) {
byteStr, err := json.Marshal(c)
if err != nil {
return nil, err
}
return string(byteStr), nil
}
func createCData() {
u1 := CUser{
Gender: 0,
Info: CInfo{
Name: "张环",
Age: 19,
},
}
DB.Model(&CUser{}).Create(&u1)
}
上述代码执行后,可以看到数据库中存储了JSON字符串
,c_users
表数据如下
json
[
{
"gender": 0,
"info": "{\"Name\":\"张环\",\"Age\":19}"
}
]
实现Scanner
接口,将从数据库中取到的值进行格式化
Go
type CInfo struct {
Name string
Age uint8
}
// Scan value 就是从数据库拿到的值
func (c *CInfo) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New("无法解析的数据")
}
err := json.Unmarshal(bytes, c)
if err != nil {
return err
}
return nil
}
func cQueryData() {
var u CUser
DB.Model(&CUser{}).First(&u)
// 打印 u is {0 {张环 19}}
fmt.Printf("u is %v\n", u)
}
完整代码
Go
package main
import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
)
type CUser struct {
Gender uint8
Info CInfo
}
type CInfo struct {
Name string
Age uint8
}
// Value c就是要向数据库中存储的值
func (c CInfo) Value() (driver.Value, error) {
byteStr, err := json.Marshal(c)
if err != nil {
return nil, err
}
return string(byteStr), nil
}
// Scan value 就是从数据库拿到的值
func (c *CInfo) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New("无法解析的数据")
}
err := json.Unmarshal(bytes, c)
if err != nil {
return err
}
return nil
}
func cAutoMigrate() {
err := DB.AutoMigrate(&CUser{})
if err != nil {
return
}
}
func createCData() {
u1 := CUser{
Gender: 0,
Info: CInfo{
Name: "张环",
Age: 19,
},
}
DB.Model(&CUser{}).Create(&u1)
}
func cQueryData() {
var u CUser
DB.Model(&CUser{}).First(&u)
fmt.Printf("u is %v\n", u)
}
func main() {
//cAutoMigrate()
//createCData()
cQueryData()
}