Skip to content
On this page

自定义数据类型

在使用GORM进行Go语言的数据库操作时,有时候会遇到内置数据类型不能满足需求的情况。可以通过实现GORM的ScannerValuer接口来创建自定义数据类型,使其能够与数据库交互。

TIP

要在GORM中使用这些自定义类型,需要实现两个接口

  • Scanner:用于从数据库读取值

  • Valuer:用于将值写入数据库

ScannerValuer接口定义如下,如果要使用自定义数据类型,需要实现ScanValue方法

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()
}