ORM
ORMとは
ORM(Object-Relational Mapping)は、[[リレーショナルデータベース]] のテーブルとプログラムのオブジェクトを対応付ける(マッピングする)仕組みです。テーブルの1行を1つのオブジェクトとして扱い、[[SQL]] を直接書かずにデータベースを操作できます。
// ORM(Prisma)の例 — SQLを書かずに検索できる
const user = await prisma.user.findUnique({ where: { id: 1 } });
// 同じ処理を生SQLで書いた場合
// SELECT * FROM users WHERE id = 1;
代表的な ORM には Prisma(Node.js)、Hibernate / JPA(Java)、Django ORM・SQLAlchemy(Python)、Active Record(Ruby on Rails)などがあり、多くの [[フレームワーク]] に組み込まれています。
何がうれしいのか
| 利点 | 内容 |
|---|---|
| 生産性 | 定型的な CRUD 操作を短いコードで書ける |
| 型安全 | オブジェクトの型とテーブル定義のズレをコンパイル時に検出できる(ORM による) |
| SQLインジェクション対策 | パラメータを自動でエスケープしてくれる |
| DB差異の吸収 | MySQL と PostgreSQL の方言の違いをある程度隠してくれる |
また、多くの ORM はテーブル定義の変更を管理する [[スキーママイグレーション]] の機能も備えており、「モデル定義を変えたら変更用SQLを自動生成する」という流れで運用できます。
落とし穴 — N+1問題
ORM 最大の定番の落とし穴が N+1問題です。一覧を1回のクエリで取得した後、各行の関連データをループの中で1件ずつ取りに行ってしまい、1+N回のクエリが発行される現象です。
SELECT * FROM posts; -- 1回(記事100件)
SELECT * FROM users WHERE id = ?; -- 記事ごとに100回発行される!
ORM がSQLを隠すために、書いた本人も気づかないまま発生します。対策は事前読み込み(eager loading — [[テーブル結合(JOIN)]] や IN でまとめて取得する指定)を使うことです。
初学者向けポイント
- ORM を使う場合でも [[SQL]] の基礎知識は必須です。「ORM が裏でどんな SQL を発行しているか」をログで確認する習慣をつけましょう。遅いクエリを見つけたら [[実行計画とクエリチューニング]] で原因を切り分けます
- 複雑な集計・レポート系のクエリは、無理に ORM で組み立てず生 SQL を書いた方が読みやすいこともあります — 使い分けが実力です
- 学習順序は「SQL を先に、ORM を後に」がおすすめです
関連技術とのつながり
- [[リレーショナルデータベース]] — ORM がマッピングする対象
- [[SQL]] — ORM が裏で生成している言語。読めないと性能問題を調査できない
- [[フレームワーク]] — 多くのWebフレームワークが ORM を標準搭載
- [[スキーママイグレーション]] — モデル定義の変更をDBへ反映する仕組み
- [[データベースインデックス]] — 発行された SQL が遅いときは EXPLAIN で実行計画を見て検討する
Q: ORMの役割として正しいのはどれ?
- [ ] データベースのバックアップを自動で取得する
- [x] テーブルとオブジェクトを対応付け、SQLを直接書かずにDB操作できるようにする
- [ ] HTTPリクエストをキャッシュする
解説: ORM はテーブルの行をオブジェクトとして扱えるようにするマッピングの仕組みです。
Q: N+1問題の説明として正しいのはどれ?
- [ ] テーブルの正規化が足りずデータが重複する問題
- [ ] インデックスを張りすぎて更新が遅くなる問題
- [x] 一覧取得後にループ内で関連データを1件ずつ取得し、クエリが大量発行される問題
解説: 1回の一覧取得+N回の個別取得でクエリが増える現象で、eager loading(事前読み込み)で対策します。
Q: 本文で推奨されている学習順序はどれ?
- [x] SQLを先に学び、その後ORMを使う
- [ ] ORMだけ学べばSQLは一切不要
- [ ] データベースより先にORMのソースコードを読む
解説: ORM が裏で発行する SQL を理解できないと性能問題を調査できないため、SQL の基礎が先です。