在开发过程中,使用GORM(Go Object-Relational Mapping)库来操作数据库是一个常见的选择,它提供了简洁的API和强大的功能,使得数据库操作更加便捷。然而,在实际应用中,开发者可能会遇到各种报错,其中之一便是关于表初始化的错误。
近期,笔者在工作中就遇到了一个关于GORM表初始化的报错,错误信息为:
invalid field found for struct xxx: define a valid foreign key for relations or implement the Valuer/Scanner interface
type Workflow struct {
Id int64
Inputs []*WorkflowInput
CreateTime int64
UpdateTime int64
}
type WorkflowInput struct{}
本文将详细探讨这一报错的原因、解决方法以及GORM标签(tag)的作用。
一、gorm表define a valid foreign key报错的原因是什么?
在深入探讨报错原因之前,我们首先需要了解GORM是如何处理结构体与数据库表之间的映射关系的。GORM允许开发者使用普通结构体来定义模型,这些结构体中的字段可以包含基本Go类型、指针或这些类型的别名,甚至是自定义类型。然而,当使用自定义类型时,GORM需要知道如何将该类型接收和保存到数据库,这就要求自定义类型实现database/sql
包中的Scanner
和Valuer
接口。
官方文档模型定义:
模型是使用普通结构体定义的。 这些结构体可以包含具有基本Go类型、指针或这些类型的别名,甚至是自定义类型(只需要实现 database/sql 包中的Scanner和Valuer接口)。
因此,如果想要支持自定义的类型,就需要手动实现Scanner和Valuer,以便让 GORM 知道如何将该类型接收、保存到数据库。
例如:
type JSON json.RawMessage
// 实现 sql.Scanner 接口,Scan 将 value 扫描至 Jsonb
func (j *JSON) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
}
result := json.RawMessage{}
err := json.Unmarshal(bytes, &result)
*j = JSON(result)
return err
}
// 实现 driver.Valuer 接口,Value 返回 json value
func (j JSON) Value() (driver.Value, error) {
if len(j) == 0 {
return nil, nil
}
return json.RawMessage(j).MarshalJSON()
}
但是由于自定义结构中存在数组,不能直接写[]struct的scan方法,所以实现后仍然会报错:
unsupported Scan, storing driver.Value type []uint8 into type *[]*WorkflowInput
二、define a valid foreign key怎么解决?
针对“define a valid foreign key for relations”这一部分,我们需要考虑如何在GORM模型中正确设置外键关系以及如何为自定义类型实现这两个接口,以便GORM能够正确地将该类型与数据库中的数据类型进行映射。接下来,我们将分别介绍这两种情况的解决方法。
- GORM 提供了一些默认的序列化器:json、gob、unixtime。直接使用提供的默认序列化器:
type Workflow struct {
Id int64
Inputs []*WorkflowInput `gorm:"type:json;serializer:json"`
CreateTime int64
UpdateTime int64
}
type WorkflowInput struct{}
- 自定义序列化器类型,将[]*workflow.WorkflowInput看成一个整体,然后手动实现Scanner和Valuer:
type Workflow struct {
Id int64
Inputs WorkflowInputs
CreateTime int64
UpdateTime int64
}
type WorkflowInputs []*WorkflowInput
type WorkflowInput struct{}
三、gorm标签作用大全
了解GORM标签(tag)的作用,能帮助用户更好地利用GORM进行数据库操作。GORM标签允许开发者在结构体字段上指定额外的信息,这些信息将影响GORM如何将该字段映射到数据库中的列。通过合理使用GORM标签,开发者可以更加灵活地控制数据库表的结构和行为。
tag | desc |
column | 指定 db 列名 |
type | 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes |
serializer | 指定序列化和反序列化方式,例如:json/gob/unixtime |
size | 指定列大小,例如:size:256 |
primaryKey | 指定列为主键 |
unique | 指定列为唯一 |
default | 指定列的默认值 |
precision | 指定列的宽度,例如:浮点数float(7,2),其中precision为7,表示不包括小数点在内的列宽,即整数部分加小数部分的总长度 |
scale | 指定列中小数部分的宽度,例如:浮点数float(7,2),其中scale为2,表示浮点数的精度,多余的小数位数会被四舍五入 |
not null | 指定列为 NOT NULL |
autoIncrement | 指定列为自动增长 |
autoIncrementIncrement | 指定列的自增步长 |
embedded | 嵌套字段 |
embeddedPrefix | 嵌入字段的列名前缀 |
autoCreateTime | 创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano |
autoUpdateTime | 创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli |
index | 根据参数创建索引,多个字段使用相同的名称则创建复合索引 |
uniqueIndex | 与 index 相同,但创建的是唯一索引 |
check | 创建检查约束,例如 check:age > 13 |
<- | 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false无写入权限、<- 创建和更新权限 |
-> | 设置字段读的权限,->:false 无读权限 |
– | 忽略该字段,- 无读写权限 |
comment | 添加字段描述信息 |
结语
GORM表初始化报错“invalid field found for struct xxx: define a valid foreign key for relations or implement the Valuer/Scanner interface”是一个涉及多个方面的问题。通过深入了解报错原因、掌握解决方法以及熟悉GORM标签的作用,我们可以更加高效地利用GORM进行数据库操作。希望本文能够为遇到类似问题的开发者提供一些帮助。
延展阅读:
如何用智能客服机器人提高产品复购率?电商商家怎么高效回复发货物流咨询?
咨询方案 获取更多方案详情