【TypeORM】プレースホルダーを使う時の地雷


環境

再現コード

  function findOneByBarAndId(
    bar: Bar
    id: number // foo の id (コード内参照) 
  ): Promise<Foo | undefined> {
    const queryBuilder = this.fooRepo
      .createQueryBuilder('foo')
      .innerJoinAndSelect('foo.bars', 'bar')
      .where('foo.id = :id', { id })
      .andWhere('bar.id = :id', { id: bar.id }) // ここに地雷があります
      .getOne()
  }

地雷

id が被っているため、前の id の値も後の値(ここでは bar.id)が使われてしまいます。
最初のプレースホルダー部分を { id: id } としても駄目です。

DBのログを有効(logging: true)にして、 同じ値が使われていたのを見て気が付きました。

地雷回避方法

プレースホルダー名を変えましょう。

-      .andWhere('bar.id = :id', { id: bar.id })  // 地雷
+      .andWhere('bar.id = :barId', { barId: bar.id }) // 回避

ドキュメント

https://github.com/typeorm/typeorm/blob/master/docs/select-query-builder.md#using-subqueries
に書かれていました。

Note: do not use the same parameter name for different values across the query builder. Values will be overridden if you set them multiple times.