TypeORM:如何实现双向关系,多个字段 --> 一种实体类型

Posted

技术标签:

【中文标题】TypeORM:如何实现双向关系,多个字段 --> 一种实体类型【英文标题】:TypeORM: how to implement bidirectional relationship, multiple fields --> one entity type 【发布时间】:2019-11-07 17:32:46 【问题描述】:

我创建了一个“文档”实体:

例如

@Entity()
export class Document 
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  path: string;
   ...


多个文档可以关联不同的实体类型:post、userProfile等

例如,在 post 实体中,我有几个字段都指定了文档关系。

  @OneToOne(type => DocumentEntity)
  @JoinColumn( name: 'default_document' )
  defaultDocument: DocumentEntity;

  @OneToOne(type => DocumentEntity)
  @JoinColumn( name: 'featured_document' )
  featuredDocument: DocumentEntity;

  @OneToMany(type => DocumentEntity, document => document.post)
  @JoinColumn( name: 'other_documents' )
  otherDocs: DocumentEntity[]; 

我不清楚如何使文档关系成为双向的。 我曾希望在文档上有一个字段,例如:

  @ManyToOne(type => abstractEntity, entity => entity.document)
  parentEntity: abstractEntity;

这样,如果我要查询文档实体的父关系, 我会得到如下结果:

documents: [

id: 1,
name: 'document 1', 
path: 'https://image.hosted.service/1.jpg', 
parentEntityId: 23
, 

id: 2
name: 'document 2', 
path: 'https://image.hosted.service/2.jpg'
parentEntityId: 27

] 

但 Typeorm 似乎希望我为 documentEntity 上的每个父关系字段定义一个完全匹配的字段,例如:

@Entity()
export class Document 
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  path: string;
  ...

  @OneToOne(type => PostEntity, post => post.defaultDocument)
  postEntityDefaultDoc: PostEntity;

  @OneToOne(type => PostEntity, post => post.featuredDocument)
  postEntityFeaturedDoc: PostEntity;

  @ManyToOne(type => PostEntity, post => post.otherDocs)
  otherDocs: PostEntity[];



为了简单起见,本示例中没有 M:N 关系:文档最多可以有一个父级。

对于父实体字段引用文档的每个可能实例,我必须在文档实体上定义一个新字段似乎不正确。 对文档的查询不会返回包含一个定义父实体的字段的列表,而是我必须解析/聚合任意数量的字段。

我似乎找不到任何教程/示例,其中单个实体具有多个字段,每个字段都引用相同的其他实体,这让我认为我的基本方法存在缺陷。

【问题讨论】:

你找到解决方案了吗? 我没有。我得出的结论是,它需要一个完整的其他连接表,我需要保持更新,这似乎很尴尬。我只通过其父实体查询文档。如果我要显示完整的文档列表,我会通过查询所有可以支持文档的实体来完成。我得出结论,这对于我的用例来说已经足够了。 【参考方案1】:

秘密成分是leftJoinAndMapMany,它允许您加入任意实体并将其映射到属性上。

这是我在你的情况下会做的。 DocumentEntity 看起来像这样:

@Entity()
class DocumentEntity 
    @PrimaryGeneratedColumn()
    public id!: number;

    @Column()
    public entity!: string;

    @Column(
        name: 'entity_id',
    )
    public entityId!: string;

    @Column()
    public name!: string;

您的PostEntity 看起来像这样:

@Entity()
class PostEntity 
    @PrimaryGeneratedColumn()
    public id!: number;

    @Column()
    public name: string;

    public documents?: DocumentEntity[];

您可能会注意到,帖子上的文档没有注释。那是因为我们将使用上述方法进行连接。您的查询看起来像这样:

connection
    .getRepository(PostEntity)
    .createQueryBuilder('p')
    .leftJoinAndMapMany(
        'p.documents',
        DocumentEntity,
        'p__d',
        '(p.id = md.entityId AND md.entity = :documentEntity)',
        
            documentEntity: PostEntity.name,
        ,
    )
    .getMany()

这些方法可用于加入这些实体:

leftJoinAndMapMany innerJoinAndMapMany leftJoinAndMapOne innerJoinAndMapOne

【讨论】:

嗨蒂姆,谢谢你的回答。但是恐怕您误解了这个问题,这可能写得不够清楚。问题不在于需要在 post 端指定额外的字段,leftJoinAndMap 确实有助于解决,而是不想在文档端创建额外的字段。但是这个元必须在某个地方定义。您仍在查询帖子,我仍然需要在文档上创建其他字段以显示文档填充的字段“角色”(对于引用文档的所有其他实体,“默认”、“特色”、“其他”等) 也许这有助于更清楚地说明:想象现在我定义了一个额外的实体“用户”。 “用户”具有字段“featuredProfile”,该字段也与文档有关系。我如何查询文档以返回所有文档及其父实体。目前我的结论是,在没有自定义连接表或某种手动“灵活”字段的 typeORM 中,这是不可能的,它存储实体类型及其 ID 列表,然后可以在自定义查询中使用,

以上是关于TypeORM:如何实现双向关系,多个字段 --> 一种实体类型的主要内容,如果未能解决你的问题,请参考以下文章

typeorm mongo db中的一对一关系

如何创建如何在 typeorm 中创建多对多关系,[NestJS]

如何在typeORM中保存@ManyToMany中的关系

TypeORM getRepository.find() 不包括外键字段

如何从控制器 JSON 返回的实体字段中排除。 NestJS + Typeorm

如何只返回 typeorm 关系的一个属性?