【问题标题】:Nest.js + Mikro-ORM: Collection of entity not initialized when using createQueryBuilder and leftJoinNest.js + Mikro-ORM:使用 createQueryBuilder 和 leftJoin 时未初始化的实体集合
【发布时间】:2020-08-14 04:00:48
【问题描述】:

我正在使用 Nest.js,并考虑从 TypeORM 迁移到 Mikro-ORM。我正在使用nestjs-mikro-orm 模块。但我被困在看似非常简单的事情上......

我有 3 个实体,AuthorEntityBookEntityBookMetadata。在我的Author 模块中,我尝试使用createQueryBuilder 方法离开加入BookBookMetadata 表。但是在运行我的查询时,我在Collection<BookEntity> of entity AuthorEntity[3390] not initialized 处遇到错误。但是,Author 表中的列可以很好地检索到。

我的 3 个实体:

@Entity()
@Unique({ properties: ['key'] })
export class AuthorEntity {
  @PrimaryKey()
  id!: number;

  @Property({ length: 255 })
  key!: string;

  @OneToMany('BookEntity', 'author', { orphanRemoval: true })
  books? = new Collection<BookEntity>(this);
}

@Entity()
export class BookEntity {
  @PrimaryKey()
  id!: number;

  @ManyToOne(() => AuthorEntity)
  author!: AuthorEntity;

  @OneToMany('BookMetadataEntity', 'book', { orphanRemoval: true })
  bookMetadata? = new Collection<BookMetadataEntity>(this);
}

@Entity()
@Unique({ properties: ['book', 'localeKey'] })
export class BookMetadataEntity {
  @PrimaryKey()
  id!: number;

  @Property({ length: 5 })
  localeKey!: string;

  @ManyToOne(() => BookEntity)
  book!: BookEntity;
}

以及我运行查询的服务文件:

@Injectable()
export class AuthorService {
  constructor(
    @InjectRepository(AuthorEntity)
    private readonly authorRepository: EntityRepository<AuthorEntity>,
  ) {}

  async findOneByKey(props: { key: string; localeKey: string; }): Promise<AuthorEntity> {
    const { key, localeKey } = props;

    return this.authorRepository
      .createQueryBuilder('a')
      .select(['a.*', 'b.*', 'c.*'])
      .leftJoin('a.books', 'b')
      .leftJoin('b.bookMetadata', 'c')
      .where('a.key = ?', [key])
      .andWhere('c.localeKey = ?', [localeKey])
      .getSingleResult();
  }
}

我错过了什么吗?可能不相关,但我也注意到使用 Nest.js 的 TypeORM 用户有 a special autoLoadEntities: true。 Mikro-ORM 有类似的东西吗?谢谢;)

【问题讨论】:

    标签: nestjs mikro-orm


    【解决方案1】:

    尚不支持从单个查询映射多个实体,计划用于 v4。你可以在这里订阅:https://github.com/mikro-orm/mikro-orm/issues/440

    在 v3 中,您需要使用 2 个查询来加载 2 个实体,对于您的用例而言,这在不涉及 QB 的情况下要容易得多。

    return this.authorRepository.findOne({ key }, ['books']);
    

    或者您可以使用qb.execute() 获取原始结果并自己映射它们,但您还必须手动为所有字段设置别名以绕过重复(Author.nameBook.name),就像 qb.select(['a.*', 'b.*'])将导致查询 select a.*, b.* ... 并且不会正确映射重复的列。

    https://mikro-orm.io/docs/query-builder/#mapping-raw-results-to-entities

    关于autoLoadEntities这个东西,没听说过,看看是怎么回事在他们的 GH repo 上。

    或者您可以使用基于文件夹的发现 (entitiesDirs)。

    下面是包含 3 个实体的新示例:

    return this.authorRepository.findOne({ 
      key,
      books: { bookMetadata: localeKey } },
    }, ['books.bookMetadata']);
    

    这将产生 3 个查询,每个 db 表一个,但第一个将自动连接书籍和 bookMetadata 以便能够通过它们进行过滤。该条件将在第二个和第三个查询中向下传播。

    如果您省略填充参数 (['books.bookMetadata']),则只会触发第一个查询,最终您将无法填充书籍(但将使用连接条件查询作者)。

    【讨论】:

    • 感谢您的快速回答!太好了,我在 Github 上订阅了这个帖子,我期待 v4!与此同时,我会仔细研究你的建议。我使用了createQueryBuilder,因为那时我想在连接表上添加where 条件。
    • 这也可以通过 EM api 实现,这要归功于自动加入:mikro-orm.io/docs/entity-manager/…
    • 您也可以使用em.populate() 填充QB 的结果:mikro-orm.io/docs/nested-populate/…
    • 谢谢!我尝试使用em.findOne API,并且能够使用where 条件创建一个工作查询。但是,我仍然无法使用任何连接列正确填充返回的对象。假设我有第三个 BookMetadata 实体,其中BookEntity 可以有很多BookMetadataEntity。我想获得 1 个作者(其中 author.key = 'foo'),并使用其所有 Books 和 BookMetadatas(其中 bookMetadata.localeKey = 'fr')填充返回的对象。实际上我仍然无法让它工作。如果不是很清楚,我已经完成了上面主要帖子中的实体。
    • 你还有.select(['a.*', 'b.*', 'c.*']),正如我所说,你不能选择多个实体,你只需要选择根。如果您想采用手动映射方式,您需要确保手动为所有可能发生冲突的字段设置别名(因此至少是 PK,因为它们都是 id)。为此使用 EM api 要好得多,因为它会自动将条件传播到填充查询。检查我答案末尾的更新示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多