使用Go的Ent ORM构建多对多关系时遇重复声明错误
Ent ORM多对多关系生成重复声明错误排查
问题描述
使用Go和Ent ORM构建多对多关系:一个SourceSpec可关联多个Tag,同一个Tag也可被多个SourceSpec使用。最初将标签模型命名为SourceSpecTag,生成了错误的一对多关系;修改为多对多并将模型重命名为Tag后,执行go generate时始终出现重复声明错误:SourceSpecTagsColumns和SourceSpecTagsTable在migrate/schema.go中被重复定义。即使删除ent目录内容、修改引用名称等操作,问题依旧,且生成的schema仍使用SourceSpecTagsTable而非TagsTable。
错误信息
{"level":"fatal","error":"entc/load: # github.com/portable-io/portable/ent/migrate\nmigrate/schema.go:679:2: SourceSpecTagsColumns redeclared in this block\n\tmigrate/schema.go:508:2: other declaration of SourceSpecTagsColumns\nmigrate/schema.go:684:2: SourceSpecTagsTable redeclared in this block\n\tmigrate/schema.go:513:2: other declaration of SourceSpecTagsTable","time":"2024-07-24T09:26:12-04:00","message":"running ent codegen"}
生成的错误schema片段
// SourceSpecTagsColumns holds the columns for the "source_spec_tags" table. SourceSpecTagsColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "tag", Type: field.TypeString, Unique: true}, } // SourceSpecTagsTable holds the schema information for the "source_spec_tags" table. SourceSpecTagsTable = &schema.Table{ Name: "source_spec_tags", Columns: SourceSpecTagsColumns, PrimaryKey: []*schema.Column{SourceSpecTagsColumns[0]}, } } // SourceSpecTagsColumns holds the columns for the "source_spec_tags" table. SourceSpecTagsColumns = []*schema.Column{ {Name: "source_spec_id", Type: field.TypeInt}, {Name: "source_spec_tag_id", Type: field.TypeInt}, } // SourceSpecTagsTable holds the schema information for the "source_spec_tags" table. SourceSpecTagsTable = &schema.Table{ Name: "source_spec_tags", Columns: SourceSpecTagsColumns, PrimaryKey: []*schema.Column{SourceSpecTagsColumns[0], SourceSpecTagsColumns[1]}, ForeignKeys: []*schema.ForeignKey{ { Symbol: "source_spec_tags_source_spec_id", Columns: []*schema.Column{SourceSpecTagsColumns[0]}, RefColumns: []*schema.Column{SourceSpecsColumns[0]}, OnDelete: schema.Cascade, }, { Symbol: "source_spec_tags_source_spec_tag_id", Columns: []*schema.Column{SourceSpecTagsColumns[1]}, RefColumns: []*schema.Column{SourceSpecTagsColumns[0]}, OnDelete: schema.Cascade, }, }, }
实体代码
package model // SourceSpec holds the schema definition for the SourceSpec entity. type SourceSpec struct { ent.Schema } // Fields of the SourceSpec. func (SourceSpec) Fields() []ent.Field { return []ent.Field{ // 省略其他字段 } } // Mixin of the SourceSpec. func (SourceSpec) Mixin() []ent.Mixin { return []ent.Mixin{ AuditTimestampsMixin{}, MutationMixin{}, } } // Tag holds the schema definition for the Tag entity. type Tag struct { ent.Schema } // Fields of the Tag. func (Tag) Fields() []ent.Field { return []ent.Field{ field.String("tag"). Unique(). NotEmpty(). StructTag(`yaml:"tag" json:"tag,omitempty"`), } } // Edges of the Tag. func (Tag) Edges() []ent.Edge { return []ent.Edge{ edge.From("source_specs", SourceSpec.Type). Ref("tags"), } }
已尝试操作
- 删除ent目录内容后重新执行
go generate - 重命名实体及相关引用
- 修改边缘引用名称
排查与解决步骤
- 补全双向多对多边缘定义
Ent ORM的多对多关系需要双向完整定义,当前仅在Tag中定义了From边缘,需在SourceSpec的Edges方法中添加对应的To边缘,并明确中间表(避免与旧表名冲突):
func (SourceSpec) Edges() []ent.Edge { return []ent.Edge{ edge.To("tags", Tag.Type). Through("source_spec_tag_assoc"), // 自定义中间表名,避免和旧的source_spec_tags冲突 } }
同时更新Tag的边缘定义,关联同一个中间表:
func (Tag) Edges() []ent.Edge { return []ent.Edge{ edge.From("source_specs", SourceSpec.Type). Ref("tags"). Through("source_spec_tag_assoc"), } }
- 明确指定Tag实体的表名
在Tag实体中添加TableName方法,强制生成正确的表名,避免Ent自动沿用旧名称:
func (Tag) TableName() string { return "tags" }
- 彻底清理生成文件与缓存
- 删除项目根目录下的
ent目录及migrate目录 - 执行
go clean -modcache清理模块缓存 - 检查项目中是否残留旧的
SourceSpecTag实体文件或引用,全部删除/替换为Tag
- 重新执行代码生成
执行go generate ./...重新生成Ent代码,此时应不会再出现重复声明错误。
内容的提问来源于stack exchange,提问作者tdev1
相关产品推荐
相关产品推荐

