TechTargetは「O/RマッピングとSQLの比較」に関する記事を公開した。O/RマッピングとSQLは、リレーショナルデータベースと“対話”するための方法だ。それぞれをどう使い分ければいいのか。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
TechTargetは2024年6月14日(米国時間)、「O/RマッピングとSQLの比較」に関する記事を公開した。
どのようなソーシャルメディアアプリケーション(以下、アプリ)、税務申告アプリや銀行アプリを取り上げても、データが保存されている場所を調べてみると、そのアプリを支えるデータベースが見つかるだろう。データベースは、企業が求める「レプリケーション(複製)」「バックアップ」「シンプルなプログラミング」「高可用性」「リアルタイムの結果」を提供する。
データベースと一言で言ってもその種類は多様だが、最も一般的なデータベース形式といえばリレーショナルモデルだろう。リレーショナルモデルのデータベース(リレーショナルデータベース 以下、RDB)の管理システム(RDBMS)では、オープンソースの「MySQL」と「PostgreSQL」が人気だ。
また、そうしたRDBMSのデータを操作する言語として「SQL」(Structured Query Language)と「O/Rマッピング」(object relational mapping)がある。どちらも「RDBと対話する言語」という意味では同じだが、どのような違いがあるのか。
RDBは、SQLを使い、テキストでデータベースを定義する。開発者は「CREATE TABLE」コマンドを使ってエンティティを定義し、「SELECT」コマンドを使用してデータを検索し、「UPDATE」「DELETE」コマンドでデータを変更する。これらのコマンドは、アプリの各種処理(作成、読み取り、更新、削除:CRUD)に対応している。
SQLの例として、'Employees'という名前のテーブルを作成し、そのテーブルに対してCRUD関数を実行する。
#サンプルテーブル CREATE TABLE Employees ( EmployeeID int NOT NULL PRIMARY KEY, LastName varchar2(50), FirstName varchar2(50), Address varchar2(255), City varchar2(255), Zip varchar2(10) ); #従業員番号を検索する SELECT Lastname, FirstName FROM Employees WHERE EmployeeID = 387; #氏名の変更、更新をする UPDATE Employees SET Lastname = "Smith" WHERE EmployeeID = 387;
SQLの場合、開発者はSELECTコマンド全体を含む文字列を作成し、その後、実行したい特定のアクションに対応する変数(例でいえば「WHERE EmployeeID = 387」の部分)を追加する必要がある。ただ、この課題はO/Rマッピングで回避できる。オブジェクト(例では、従業員)をエンティティ(例では、従業員テーブル)に明示的に結び付ければいい。
O/Rマッピングを使ってオブジェクトをエンティティに関連付けるには、まず、データベーステーブルにエンティティと同じメンバーを持つオブジェクト(例では、FirstName、LastName、EmployeeID)を定義する。次に、設定ファイル(恐らくXML形式を使うことになるだろう)を追加する。この設定ファイルは、オブジェクトをテーブルにマッピングし、どのメンバー変数がどのテーブル属性に接続するかを示し、データベースの場所を指定する。最後に、コンパイル時に特別な指示を与える魔法のような構文(Javaのアノテーション)が少しあるかもしれない。これらはJava自体にとってはあまり重要ではないが、データベースにとっては重要になる可能性がある。例えば、「新しいレコードが作成されるときにはEmployeeIDを自動生成すること」をO/Rマッピングのフレームワークが認識していないければならない場合がある。
O/Rマッピングを使えば、例えば以下のようなJavaのコードで新しい従業員を簡単に作成できるようになる。
Employee emp = new employee("Jackson","Sarah", "123 Easy St", "Madison", "WI", "53558");
もちろん、更新も容易だ。
emp.LastName = "Brown";
ただ、ほとんどのデータベースにはトランザクションがあるため、これほど単純にはいかないだろう。トランザクションが複雑なほど、データの整合性に注意する必要がある。例えば従業員の情報には、IDや名前以外に給与等級や職務コードなどがあるが、それらを別のテーブルで管理しなければならない場合がある。そうした場合にトランザクションが有効だ。トランザクションは複数の操作をまとめ、変更の全てが成功したかどうかをチェックできる。そのため、開発者は失敗した時の処理(例外処理)をあらかじめ設定できるようになる。
O/Rマッピングには、開発者が自分でトランザクションを構築する必要があるものもあれば、テーブル全体をメモリにマッピングし、変更内容を追跡し、必要に応じてバックグラウンドでデータベースに自動的に保存できるものもある。これらをうまく使えば、特にデスクトップアプリにおいてデータベースやネットワークのやりとりを節約できる。
O/RマッピングはSQLと違い、Java、「C#」「Python」などでコードを書くような感覚でデータベースオブジェクトを処理できる。前述したようにSQLの場合はデータの取得、変更をする際に変数を追加する必要がある。
O/Rマッピングを使ってレコードを取得する例を紹介しよう。この例では、JavaのO/Rマッピングフレームワーク「Hibernate」を使い、「EmployeeID」フィールドが「3」の従業員を取得する。
Criteria cr = session.createCriteria(Employee); cr.add(Restrictions.eq("EmployeeID", 3)); List employee = cr.list();
O/Rマッピングの利点は、データベースが抽象化されるため、開発者が詳細に設定しなくてもよいことだ。アプリが概略化されると、メンテナンス用のプログラミングも簡単になる。APIによっては、O/Rマッピングがデータを遅延ロード(必要になるまで読み込みを遅くすること)したり、データベースから事前にフェッチ(予測されるデータを呼び出す)したりできる。
一方で、データベースが抽象化されてしまうという課題もある。つまり、開発者はデータベースで何が起こっているのか正確には制御できなくなるということだ。事前フェッチ(プリフェッチ)するO/Rマッピングの場合、特にデスクトップアプリにおいては、フェッチするデータが多過ぎて、メモリの問題や遅延が発生する可能性がある。
著者は、かつてテーブル全体をロードするO/Rマッピングを使った時、実際に“遅延”を経験したことがある。ソフトウェアはWebページ上でデータを視覚化し、始めは高速だったが、数千行に達すると遅延が発生したのだ。
O/Rマッピングは基本的に全てのオブジェクトから要素を作成するため、データベースのサイズが増加するにつれてこの遅延は直線的に増加した。データベースのレコードが数万行レベルになると、ソフトウェアは実質的に使用できなくなった。そのため、O/Rマッピングは複数の結合を含むような複雑なクエリの処理は困難になる。また、O/Rマッピングが一度に1つのデータベースしかサポートしない場合、データベースインスタンス間のクエリができないことになる。
純粋なSQLでのプログラミングは効率的、直接的にデータベースとやりとりできる方法だ。少なくとも、O/Rマッピングより「ハードウェアに近い」と言える。SQLを使用すると、実行速度が遅いクエリのデバッグやデータベースのパフォーマンス問題の修正、バグのあるコードの修正が簡単になる。これは開発者が、データベースログ内の特定の呼び出しを、特定のSQLコマンドレベルまでさかのぼって追跡できるためだ。また、開発者はコードを直接取り出して、SQLクエリなどが実行できるツール(ワークベンチ)で試すこともできる。ただ、クエリのデバッグやパフォーマンスチューニングなどのスキルは一般的なプログラミングスキルとは異なるため、全ての開発者ができるわけではない。
一部のO/Rマッピングは独自のクエリ言語を提供しており、それはSQLに近い(もしくはSQLと同一な)こともある。これは主に複雑な結合において有用だ。
遅延ロードの例のような、テーブルを一覧表示するような単純なレポートであればSQLで作成する方が簡単で、高速だ。一方、小規模なCRUDアプリならO/Rマッピングの方が簡単だろう。どちらもマルチユーザーに対応できるが、比較的小規模な環境、例えばWebベースのサービスにおいて、数百人程度の顧客サービス担当者が利用するような場合に向いている。ただ、Webスケールでは、REST APIは各リクエストを個別に処理する傾向があり、それぞれが1つのトランザクションとして扱われるため、プリフェッチや自動計算されたトランザクション処理の利点が失われてしまう可能性がある。
開発初心者にとってはO/Rマッピングの方が簡単かもしれない。O/Rマッピングが導入されると、メンテナンスが簡単になる。だが、新しいオブジェクトを関連するテーブルに接続するときには幾つかの“配線作業”が必要になる。
O/Rマッピングのインストール、テーブルの接続、データベースへの接続方法の検討などの“配線作業”では、データベースとパスワードをシークレットファイルに保存する必要があり、テスト環境のレベルに基づいてどのデータベースを呼び出すかを変更する必要があるため、特定のプログラミング環境の特異性に精通した開発者が担当した方がいいだろう。
一から始めるなら、恐らくSQLの方が取り掛かりやすい。ただし、開発者はSQLにおける一般的なデータアクセスパターンを学ぶ必要がある。またSQLは、動作はするものの保守が難しい“泥団子のようなコード”ができやすいという特徴がある。一方のO/Rマッピングも、概念的な限界に達しやすく、それ以上の拡張が困難になったり、意図しない結果を招いたりする可能性がある。
忘れてはならないのが、ほぼ全てのプログラミング言語は、何らかのデータベースコネクターを介してSQLをサポートしているいということだ。SQLを挿入すると、単一行の配列や、複数行を含むオブジェクトが返される。開発者はそれをオブジェクトに変換するか、画面に表示する必要がある。
反対に、幅広い言語間でサポートされているO/Rマッピングは事実上存在しない。つまり、チームがサポートする各プログラミング言語に対して、それぞれ異なるO/Rマッピングが必要になる可能性が高い。もちろん「.NET」のような同一プラットフォーム上の言語は例外となる可能性がある。
大規模な組織には、さまざまなコンテキストを持つ、さまざまなチームが存在する。特に、買収によって成長した企業ではそれが顕著だ。そうした企業や組織では、全てのアプリケーションに単一の標準を強制する代わりに、広範なポリシーを考え出す方が理にかなっているだろう。組織は、例えばレガシーソフトウェアのデータベースのサポートを継続しつつ、内部システムとデスクトップソフトウェアにはO/Rマッピングを使用し、マイクロサービスとAPIにはSQLを使い、複雑なクエリにはハイブリッド(SQLとO/Rマッピングの併用)のアプローチを採用するかもしれないからだ。
そのため、小さなことから始めて、将来と複雑さに注意深く目を向け、概念実証(PoC)を使用してSQLとO/Rマッピングを実験するのがいいだろう。結局のところ、問題は、組織のために何が機能しているのか、そして次に試みるべき小さな実験は何かということだ。
Copyright © ITmedia, Inc. All Rights Reserved.