LoginSignup
6
1

More than 1 year has passed since last update.

TypeORMでQueryBuilderを使わずにJOIN先のテーブルのカラムをWhere句で使用する

Last updated at Posted at 2021-10-07

注意

この記事の公開から時間が経っていたり、typeormのバージョンが異なる場合は、仕様が異なる場合があります。

動作確認: 2021/10/7
typeorm: v0.2.34

TL;DR

JOIN先の主キー(ID)で絞り込む場合

const teenProgrammers = await getRepository(User).find({
  relations: ['role'], // なくてもOK
  where: {
    age: LessThanOrEqual(19),
    role: {
      id: programmer.id
    }
  }
})

JOIN先の主キー(ID)以外で絞り込む場合

const teenProgrammers = await getRepository(User).find({
  join: {
    alias: "user",
    innerJoin: { role: "user.role" },
  },
  where: (qb: SelectQueryBuilder<User>) => {
    qb.where({ age: LessThanOrEqual(19) }).andWhere(
      "role.name = :roleName",
      { roleName: "programmer" }
    );
  },
});

前提条件

エンティティ

entity/role.ts
@Entity()
export class Role {
    @PrimaryGeneratedColumn()
    id?: number;

    @Column()
    name: string;

    @OneToMany(() => User, user => user.id)
    users: User[];
}
entity/user.ts
@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id?: number;

    @Column()
    name: string;

    @Column()
    age: number;

    @ManyToOne(() => Role)
    role?: Role
}

メイン

index.ts
createConnection().then(async connection => {
  const [programmer, boxer] = await getRepository(Role).save([
    {
      name: 'programmer'
    },
    {
      name: 'boxer'
    }
  ])

  await getRepository(User).save([
    {
      name: 'alice',
      age: 18,
      role: programmer
    },
    {
      name: 'bob',
      age: 55,
      role: boxer
    },
    {
      name: 'carol',
      age: 30,
      role: programmer
    }
  ])
})

}).catch(error => console.log(error))

データベース内の状態

role

id name
1 programmer
2 boxer

user

id name age roleId
1 alice 18 1
2 bob 55 2
3 carol 30 1

他の方法

クエリビルダを使う

表現力が高くSQLライクなのはいいですが、冗長です。

const teenProgrammers = await getRepository(User)
  .createQueryBuilder("user")
  .innerJoinAndSelect("user.role", "role")
  .where("role.id = :roleId", { roleId: programmer.id })
  .andWhere("age <= 19")
  .getMany();

できない例

SQLも問題なく生成されますが、パラメータにnullが渡されてしまうのでダメです。

const teenProgrammers = await getRepository(User).find({
  relations: ["role"],
  where: {
    age: LessThanOrEqual(19),
    "role.id": programmer.id,
  },
});

発行されるSQL(抜粋)

LEFT JOIN "role" "User__role"
ON "User__role"."id"="User"."roleId"
WHERE "User"."age" <= ?
AND "User"."roleId" = ?
-- PARAMETERS: [19,null]

ID以外をQBを使わずに指定するとダメ

この場合も発行されるSQLと渡されるパラメータは上記と同じでroleId = nullになります。

const teenProgrammers = await getRepository(User).find({
  relations: ["role"],
  where: {
    age: LessThanOrEqual(19),
    role: {
      name: "programmer",
    },
  },
});

QBとrelationsを併用すると微妙

role.nameと指定することはできません。

const teenProgrammers = await getRepository(User).find({
  relations: ["role"],
  where: (qb: SelectQueryBuilder<User>) => {
    qb.where({ age: LessThanOrEqual(19) }).andWhere(
      "role.name = :roleName",
      { roleName: "programmer" }
    );
  },
});
FROM "user" "User"
LEFT JOIN "role" "User__role"
ON "User__role"."id"="User"."roleId"
WHERE "User"."age" <= ?
AND role.name = ? -- PARAMETERS: [19 "programmer"]

User__role.nameと書けば冒頭のコードと同様に動作しますが、内部的に自動生成された(されるはずの)文字列を指定するのは微妙な気がします。

const teenProgrammers = await getRepository(User).find({
  relations: ["role"],
  where: (qb: SelectQueryBuilder<User>) => {
    qb.where({ age: LessThanOrEqual(19) }).andWhere(
      "User__role.name = :roleName",
      { roleName: "programmer" }
    );
  },
});

参考

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1