ORM
プログラムのオブジェクトとDBの表を対応づけ、SQLを書かずにDBを操作する道具。
概要
ORM(Object-Relational Mapping)は、プログラミング言語のオブジェクトとRDBMSのテーブルを対応づけ、SQLを直接書かずにデータベースを操作できるようにするライブラリです。「Userクラスはusersテーブルに対応し、user.save() を呼べばINSERT文が発行される」という写像(マッピング)を自動化してくれます。
RailsのActiveRecord、PythonのSQLAlchemy、JavaのHibernate、TypeScriptのPrismaやDrizzleなど、主要な言語にはほぼ必ず定番のORMが存在します。現代のWebアプリケーション開発では、データベースアクセスの大半をORM経由で書くのが一般的で、バックエンド開発者が毎日触れる道具の一つです。
なぜ生まれたか
オブジェクト指向言語とリレーショナルデータベースは、データの捉え方が根本的に異なります。プログラム側は参照でつながったオブジェクトのグラフとしてデータを扱い、データベース側は正規化された表と外部キーで扱います。この食い違いは「インピーダンスミスマッチ」と呼ばれ、両者を橋渡しするコード——SQLを組み立て、結果の行をオブジェクトに詰め替え、変更を検出してUPDATE文に翻訳する——を、開発者はテーブルごとに延々と手書きしていました。
この定型作業は量が多いだけでなく、バグの温床でもありました。列を1つ追加するたびにSQLと詰め替えコードを何か所も修正し、文字列連結でSQLを組み立てればSQLインジェクションの危険が生まれます。ORMはこの橋渡しをライブラリとして一般化し、マッピングの定義さえ書けば変換コードを自動生成・自動実行する形で解決しました。2000年代のHibernateとRails(ActiveRecord)の成功により、Web開発の標準装備として定着しました。
詳細
ORMが肩代わりする仕事
ORMの中核は、オブジェクト操作からSQLへの翻訳です。User.find(1) はSELECT文に、プロパティを変更しての save() はUPDATE文に、オブジェクトの削除はDELETE文に変換されます。値は必ずプレースホルダ経由で渡されるため、SQLインジェクション対策が構造的に組み込まれます。関連の定義(「Userは複数のPostを持つ」)を書いておけば、user.posts とたどるだけで裏でJOINや追加のSELECTが発行されます。
多くのORMは周辺の仕事も担います。スキーマ変更を差分ファイルとして管理するマイグレーション、複数の操作をトランザクションで括るAPI、発行されたSQLのログ出力などです。PrismaやDrizzleのようにスキーマ定義から型を生成し、存在しない列へのアクセスをコンパイル時に検出できるものもあり、TypeScript環境では型安全性が選定の大きな基準になっています。
2つの設計パターン
ORMの設計には大きく2つの流派があります。ActiveRecordパターンは「オブジェクト自身が save() や delete() を持つ」スタイルで、直感的に書ける反面、モデルクラスがDB都合と業務ロジックの両方を抱えがちです。Data Mapperパターン(Hibernate、SQLAlchemy、TypeORMの一方のモード)は、オブジェクトと永続化の責務を分離するスタイルで、複雑なドメインには向くものの記述量は増えます。近年はPrismaのように「マッピング」より「型付きクエリビルダ」に軸足を置く、より薄い設計も広がっています。
N+1問題という代表的な罠
ORM最大の落とし穴がN+1問題です。「投稿を100件取得し、それぞれの著者名を表示する」コードを素直に書くと、投稿一覧の1クエリに加えて、ループ内で著者取得のクエリが100回発行されます。1回のJOINで済む処理が101回のクエリになり、コード上は1行のループなので気づきにくいのが厄介です。各ORMには関連の事前読み込み(eager loading。ActiveRecordの includes、Prismaの include など)が用意されており、「ループ内で関連をたどっていないか」を意識するのがORM使いの基本動作です。
便利さの代償と付き合い方
ORMの本質的なトレードオフは「SQLが見えなくなる」ことです。生成されるSQLが非効率でも気づきにくく、インデックスが効かない条件式を吐いていても表面上は動いてしまいます。対策はシンプルで、開発中はクエリログを出して実際のSQLを確認し、遅い箇所はEXPLAINで実行計画を見ることです。ORMはSQLを知らなくて済む道具ではなく、SQLを知っている人の反復作業を減らす道具だと捉えるのが健全です。
また、複雑な集計や大量データの一括更新など、ORMの抽象化が合わない処理は無理にORMで書かず、生SQLに切り替える判断も重要です。主要なORMはどれも生SQLへの脱出口を備えています。「9割の定型的なCRUDはORMで素早く、1割の勝負どころは手書きのSQLで」という使い分けが、実務での現実的な落としどころです。