TypeORMのQueryBuilderの基本的な使い方

## はじめに
TypeORMのQueryBuilderの基本的な使い方のまとめです。詳しくは[こちら](https://typeorm.io/#/select-query-builder)に公式のドキュメントがあります。

## QueryBuilderとは
[TypeORM](https://typeorm.io/#/)で提供されている**QueryBuilder**はカスタマイズ性が高い、SQLを構築するためのクラスです。

TypeORMの`EntityManager`や`Repository`の標準のメソッド（find、findOne、saveなど）だけでは実現が難しい、複数テーブルを跨いだ処理などがある場合、TypeORMのQueryBuilderを使うのも一つの選択肢です。

ただしQueryBuilderを使うということは生のSQLの記述により近くなり、そもそもORMを使っているのにSQLを書くようなイメージで、特に理由がなければ標準のメソッドだけで実装するのが良さそうです。カスタマイズ性が高い分、自分でしっかり設計した上で実装しないとパフォーマンスに影響が出る可能性もあり、後に説明するのですが、SQLインジェクションなど、標準のメソッドを使う際にはあまり気にしなくてもよい部分も考慮する必要があったりします。


## QueryBuilderのパターン

QueryBuilderの実装パターンには以下の3種類があります。

### Connectionを使ったやり方
`from`の第一引数に対象のデータモデルのクラスを指定します（第二引数はSQLでのalias）。

```typescript
import { getConnection } from "typeorm";

const user = await getConnection()
    .createQueryBuilder()
    .select("user")
    .from(User, "user")
    .where("user.id = :id", { id: 1 })
    .getOne();
```

### Entity Managerを使ったやり方

`createQueryBuilder `の第一引数に対象のデータモデルのクラスを指定します（第二引数はSQLでのalias）。

```typescript
import { getManager } from "typeorm";

const user = await getManager()
    .createQueryBuilder(User, "user")
    .where("user.id = :id", { id: 1 })
    .getOne();
```

### Repositoryを使ったやり方
`getRepository `の引数に対象のデータモデルのクラスを指定します（`createQueryBuilder`の引数はSQLでのalias）。

```typescript
import { getRepository } from "typeorm";

const user = await getRepository(User)
    .createQueryBuilder("user")
    .where("user.id = :id", { id: 1 })
    .getOne();
```

## 基本的なメソッド
基本的なメソッドには以下の4つがあります。

### Select
`getOne`や`getMany`メソッドで繋ぎ、単一もしくは複数レコードを取得（`findOne`、`find`と同様の返り値）

```typescript
import { getConnection } from "typeorm";

const user = await getConnection()
    .createQueryBuilder()
    .select("user")
    .from(User, "user")
    .where("user.id = :id", { id: 1 })
    .getOne();
```

### Insert
`values`で値を渡し、`execute`でレコードの保存（`save`と同様の返り値）

```typescript
import { getConnection } from "typeorm";

await getConnection()
    .createQueryBuilder()
    .insert()
    .into(User)
    .values([
        { firstName: "Timber", lastName: "Saw" },
        { firstName: "Phantom", lastName: "Lancer" }
     ])
    .execute();
```

### Update
`set`で値を渡し、`execute`でレコードの更新（`save`と同様の返り値）

```typescript
import { getConnection } from "typeorm";

await getConnection()
    .createQueryBuilder()
    .update(User)
    .set({ firstName: "Timber", lastName: "Saw" })
    .where("id = :id", { id: 1 })
    .execute();
```

### Delete
`where` で対象となるレコードを指定し、`execute`で削除

```typescript
import { getConnection } from "typeorm";

await getConnection()
    .createQueryBuilder()
    .delete()
    .from(User)
    .where("id = :id", { id: 1 })
    .execute();
```

## Raw Results

TypeORMの`Entity`クラスで定義したデータモデルとは異なる形のオブジェクトが返却される場合には`getRawOne`もしくは`getRawMany`メソッドを使います。

```typescript
// getRawOneに型を渡さないと返却される値がanyになる
interface Sum {
  sum: number;
}

const { sum } = await getRepository(User)
    .createQueryBuilder("user")
    .select("SUM(user.photosCount)", "sum")
    .where("user.id = :id", { id: 1 })
    .getRawOne<Sum>(); // { sum: 100 }
```
```typescript
// getRawManyに型を渡さないと返却される値がanyの配列になる
interface Sum {
  id: number;
  sum: number;
}

const photosSums = await getRepository(User)
    .createQueryBuilder("user")
    .select("user.id")
    .addSelect("SUM(user.photosCount)", "sum")
    .groupBy("user.id")
    .getRawMany<Sum[]>(); // [{ id: 1, sum: 25 }, { id: 2, sum: 13 }, ...]
```

## SQLインジェクション対策
`where`などの第二引数にパラメータのオブジェクトを渡すことで対象の値をエスケープすることができます。これによりユーザーの入力値などによるSQLインジェクションを防止します。

```typescript
.where("user.name = :name", { name: "Timber" })
```

配列の値もエスケープが可能。

```typescript
.where("user.name IN (:...names)", { names: [ "Timber", "Cristal", "Lina" ] })
```

## まとめ
以上、QueryBuilderの基本的な使い方でした。ちなみに`.createQueryBuilder()`ではなく、`.query()`で引数に生のSQLを投入することもできます。複数テーブルのデータを統合するなど、いざという時に知っておくと力を発揮するかもしれません。