Entgo基于code_delivery_rk配置O2M关联报错,求解决方案
Entgo一对多Edge关联配置问题解决
问题说明
需要给两个Entgo Schema配置一对多(O2M)关联:
- BridgeVersion(主表)、LinkServiceDelivery(关联表)
- 关联逻辑:一个
BridgeVersion的version_rk对应多个LinkServiceDelivery的service_rk - 查询需求:通过
WithLinkDelivery()基于code_delivery_rk筛选
配置Edge时持续触发报错:
has a field "code_delivery_rk" but it is not holding a foreign key
目前临时通过视图+基于version_rk的常规关联解决,但认为这不是合规方案,求正确的Edge配置方式。
现有Schema代码
BridgeVersion
package schemas import ( "entgo.io/ent" "entgo.io/ent/dialect" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) type BridgeVersion struct { ent.Schema } func (BridgeVersion) Fields() []ent.Field { return []ent.Field{ field.Int("id").SchemaType(map[string]string{ dialect.Postgres: "bigserial", }).StorageKey("version_rk"), field.Int("code_delivery_rk"), field.Int("release_major_num"), field.Int("release_minor_num"), field.Int("release_fix_num"), field.Text("release_desc"), field.Time("release_build_dttm").SchemaType(map[string]string{ dialect.Postgres: "timestamp with time zone", }), field.Time("release_delivery_dttm").SchemaType(map[string]string{ dialect.Postgres: "timestamp with time zone", }), } } func (BridgeVersion) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Annotation{Table: "bridge_version", Schema: "metamodel"}, } } func (BridgeVersion) Edges() []ent.Edge { return []ent.Edge{ edge.To("bridge_flow", BridgeFlow.Type), edge.From("dict_code_delivery", DictCodeDelivery.Type).Ref("bridge_version").Field("code_delivery_rk").Required().Unique(), edge.From("link_service_delivery", LinkServiceDelivery.Type).Ref("bridge_version").Field("code_delivery_rk"), //edge.To("bridge_service", BridgeService.Type).Through("link_service_delivery", LinkServiceDelivery.Type).Field("code_delivery_rk"), //edge.To("link_service_delivery", LinkServiceDelivery.Type)., //edge.To("group", BridgeVersion.Type), edge.To("flow_version_delivery", FlowVersionDelivery.Type).StorageKey(edge.Column("version_rk")), } }
LinkServiceDelivery
package schemas import ( "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) type LinkServiceDelivery struct { ent.Schema } func (LinkServiceDelivery) Fields() []ent.Field { return []ent.Field{ field.Int("id").StorageKey("service_rk"), field.Int("code_delivery_rk").StorageKey("code_delivery_rk"), field.Bool("is_ddl_flg"), field.Bool("is_flow_flg"), field.Bool("is_config_flg"), } } func (LinkServiceDelivery) Edges() []ent.Edge { return []ent.Edge{ //edge.From("bridge_flow", BridgeFlow.Type).Ref("service").Through("t2", BridgeVersion.Type), //edge.To("bridge_service", BridgeService.Type).Unique().Required().Field("service_rk"), edge.To("bridge_version", BridgeVersion.Type).Field("code_delivery_rk"), } } func (LinkServiceDelivery) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Annotation{Table: "link_service_delivery", Schema: "metric"}, } }
报错原因
核心问题有两个:
- 使用
.Field("code_delivery_rk")时,Entgo要求该字段必须是指向关联表主键的外键,但当前code_delivery_rk只是普通字段,不满足外键约束 - 关联逻辑与代码配置混淆:你描述的是
BridgeVersion.version_rk对应LinkServiceDelivery.service_rk,但代码里却在用code_delivery_rk绑定关联,导致映射逻辑混乱
正确配置方案
调整后的BridgeVersion Schema
package schemas import ( "entgo.io/ent" "entgo.io/ent/dialect" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) type BridgeVersion struct { ent.Schema } func (BridgeVersion) Fields() []ent.Field { return []ent.Field{ field.Int("id").SchemaType(map[string]string{ dialect.Postgres: "bigserial", }).StorageKey("version_rk").Unique(), // 明确主键唯一性 field.Int("code_delivery_rk"), field.Int("release_major_num"), field.Int("release_minor_num"), field.Int("release_fix_num"), field.Text("release_desc"), field.Time("release_build_dttm").SchemaType(map[string]string{ dialect.Postgres: "timestamp with time zone", }), field.Time("release_delivery_dttm").SchemaType(map[string]string{ dialect.Postgres: "timestamp with time zone", }), } } func (BridgeVersion) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Annotation{Table: "bridge_version", Schema: "metamodel"}, } } func (BridgeVersion) Edges() []ent.Edge { return []ent.Edge{ edge.To("bridge_flow", BridgeFlow.Type), edge.From("dict_code_delivery", DictCodeDelivery.Type).Ref("bridge_version").Field("code_delivery_rk").Required().Unique(), // 配置一对多正向关联:一个BridgeVersion对应多个LinkServiceDelivery edge.To("link_service_deliveries", LinkServiceDelivery.Type). StorageKey(edge.Column("code_delivery_rk")), // 指定关联字段为LinkServiceDelivery的code_delivery_rk edge.To("flow_version_delivery", FlowVersionDelivery.Type).StorageKey(edge.Column("version_rk")), } }
调整后的LinkServiceDelivery Schema
package schemas import ( "entgo.io/ent" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" ) type LinkServiceDelivery struct { ent.Schema } func (LinkServiceDelivery) Fields() []ent.Field { return []ent.Field{ field.Int("id").StorageKey("service_rk").Unique(), // 明确主键 field.Int("code_delivery_rk"), // 字段名与存储列名一致,可省略重复的StorageKey field.Bool("is_ddl_flg"), field.Bool("is_flow_flg"), field.Bool("is_config_flg"), } } func (LinkServiceDelivery) Edges() []ent.Edge { return []ent.Edge{ // 配置反向关联:多个LinkServiceDelivery属于一个BridgeVersion edge.From("bridge_version", BridgeVersion.Type). Ref("link_service_deliveries"). Field("code_delivery_rk"). // 指定存储在当前表的关联字段 Required(), // 根据业务需求决定是否必填 } } func (LinkServiceDelivery) Annotations() []schema.Annotation { return []schema.Annotation{ entsql.Annotation{Table: "link_service_delivery", Schema: "metric"}, } }
关键调整点
- 关联方向明确:
- 主表用
edge.To定义一对多关系(一个主表对应多个关联表) - 关联表用
edge.From定义反向关联(多个关联表归属一个主表)
- 主表用
- 字段映射修正:
- 使用
StorageKey(edge.Column("xxx"))指定关联字段,而非用.Field()绑定普通字段(.Field()是用来定义Edge的额外存储字段,不是外键关联)
- 使用
- 主键声明:给两个表的
id字段添加.Unique(),确保Entgo识别为主键 - 冗余配置清理:删除重复的
StorageKey配置,字段名与数据库列名一致时可省略
查询使用示例
配置完成后,可通过以下代码实现基于code_delivery_rk的关联查询:
// 查询指定code_delivery_rk的BridgeVersion及其关联的LinkServiceDelivery versions, err := client.BridgeVersion. Query(). Where(bridgeversion.CodeDeliveryRK(123)). WithLinkServiceDeliveries(). All(ctx)
内容的提问来源于stack exchange,提问作者Дмитрий Пырин
相关产品推荐
相关产品推荐

