42
16

More than 1 year has passed since last update.

TypeORMのQueryBuilderの基本的な使い方

Last updated at Posted at 2021-02-25

はじめに

TypeORMのQueryBuilderの基本的な使い方のまとめです。詳しくはこちらに公式のドキュメントがあります。

QueryBuilderとは

TypeORMで提供されているQueryBuilderはカスタマイズ性が高い、SQLを構築するためのクラスです。

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

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

QueryBuilderのパターン

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

Connectionを使ったやり方

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

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)。

import { getManager } from "typeorm";

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

Repositoryを使ったやり方

getRepository の引数に対象のデータモデルのクラスを指定します(createQueryBuilderの引数はSQLでのalias)。

import { getRepository } from "typeorm";

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

基本的なメソッド

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

Select

getOnegetManyメソッドで繋ぎ、単一もしくは複数レコードを取得(findOnefindと同様の返り値)

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と同様の返り値)

import { getConnection } from "typeorm";

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

Update

setで値を渡し、executeでレコードの更新(saveと同様の返り値)

import { getConnection } from "typeorm";

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

Delete

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

import { getConnection } from "typeorm";

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

Raw Results

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

// 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 }
// 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インジェクションを防止します。

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

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

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

まとめ

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

42
16
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
42
16