📦GORMv1.25.6

🏆 自定义数据

⭐️ 在有些时候,数据库内单单存储基本数据类型往往不够,可能需要存储 json这种类型

🍅 实习自定义数据类型

🌟 Scanner / Valuer

自定义的数据类型必须实现 ScannerValuer 接口,以便让 GORM 知道如何将该类型接收、保存到数据库

结构体这个类型就和json非常相似,可以直接通过 json标签来指定是 json类型的,然后后面接的是在 json中存储的字段

type Task struct {
	Status  string `json:"status"`
	Title   string `json:"title"`
	Context int    `json:"context"`
}

// Scan 从数据库中读取出来
func (i *Task) Scan(value interface{}) error {
    //存储的时候使用断言转换为byte
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
    //使用字符串存入
	err := json.Unmarshal(bytes, i)
	return err
}

// Value 存入数据库
func (i Task) Value() (driver.Value, error) {
    //将字符串转换为json读出
	return json.Marshal(i)
}

type User struct {
	gorm.Model
	Name     string
	Password string
	Task     Task `gorm:"type:string;"`
}

创建对象并插入

func main() {
	// 连接数据库
	db, err := untils.Dbuntils()
	if err != nil {
		println("连接数据库失败", err.Error())
	}

	createTablesError := db.AutoMigrate(&User{})
	if createTablesError != nil {
		fmt.Println("创建表失败", createTablesError.Error())
	}

	u := &User{
		Name:     "zhangsan",
		Password: "123456",
		Task: Task{
			Status:  "1",
			Title:   "title",
			Context: "学习Gorm",
		},
	}

	db.Debug().Create(u)

}


执行完成查看数据语句

INSERT INTO `gormuser` (`created_at`,`updated_at`,`deleted_a
t`,`name`,`password`,`task`) VALUES ('2024-07-03 20:34:22.14','2024-07-03 20:34:
22.14',NULL,'zhangsan','123456','{"status":"1","title":"title","context":"学习Go
rm"}') RETURNING `id`

MariaDB [gorm]> select * from gormuser;
+----+-------------------------+-------------------------+------------+----------+----------+-------------------------------------------------------+
| id | created_at              | updated_at              | deleted_at | name     | password | task                                                  |
+----+-------------------------+-------------------------+------------+----------+----------+-------------------------------------------------------+
|  1 | 2024-07-03 20:34:22.140 | 2024-07-03 20:34:22.140 | NULL       | zhangsan | 123456   | {"status":"1","title":"title","context":"学习Gorm"}   |
+----+-------------------------+-------------------------+------------+----------+----------+-------------------------------------------------------+
1 row in set (0.002 sec)

MariaDB [gorm]>

🍅 GormDataTypeInterface

GORM 会从 type 标签 中读取字段的数据库类型,如果找不到,则会检查该结构体是否实现了 GormDBDataTypeInterfaceGormDataTypeInterface 接口,然后使用接口返回值作为数据类型

type GormDataTypeInterface interface {
  GormDataType() string
}

type GormDBDataTypeInterface interface {
  GormDBDataType(*gorm.DB, *schema.Field) string
}

GormDataType 的结果用于生成通用数据类型,也可以通过 schema.FieldDataType 字段得到。这在 编写插件 或者 hook 时可能会有用,例如:

func (JSON) GormDataType() string {
  return "json"
}

type User struct {
  Attrs JSON
}

func (user User) BeforeCreate(tx *gorm.DB) {
  field := tx.Statement.Schema.LookUpField("Attrs")
  if field.DataType == "json" {
    // 做点什么...
  }
}

在迁移时,GormDBDataType 通常会为当前驱动返回恰当的数据类型,例如:

func (JSON) GormDBDataType(db *gorm.DB, field *schema.Field) string {
  // 使用 field.Tag、field.TagSettings 获取字段的 tag
  // 查看 https://github.com/go-gorm/gorm/blob/master/schema/field.go 获取全部的选项

  // 根据不同的数据库驱动返回不同的数据类型
  switch db.Dialector.Name() {
  case "mysql", "sqlite":
    return "JSON"
  case "postgres":
    return "JSONB"
  }
  return ""
}

如果 struct 没有实现 GormDBDataTypeInterfaceGormDataTypeInterface 接口,GORM 会根据 struct 第一个字段推测其数据类型,例如:会为 NullString 使用 string

type NullString struct {
  String string // 使用第一个字段的数据类型
  Valid  bool
}

type User struct {
  Name NullString // 数据类型会是 string
}