はじめに
データベースを使用してアプリケーションデータを管理することは、データの永続性に関する最も一般的な選択肢の1つです。データベースは、高速な情報の保存と取得を可能にし、データの整合性を保証し、個々のアプリケーションインスタンスの存続期間を超えた永続性を提供します。プロジェクトの要件と好みを満たすために利用できるデータベースには、数え切れないほどの種類があります。
ただし、アプリケーションからデータベースを直接操作するのは必ずしも簡単ではありません。データ構造の表現方法の違いは、多くの場合、課題につながります。異なるエンティティ間の関係についての微妙な表現の難しさも問題を引き起こす可能性があります。これに対処するために、コアアプリケーションとデータレイヤー間のインターフェイスとして機能するためのさまざまなツールが作成されています。
このガイドでは、生のSQL、クエリビルダー、およびORM(オブジェクトリレーショナルマッパー)の3つの一般的なアプローチの間に生じるいくつかの違いを見ていきます。各アプローチの長所と短所のいくつかを比較してから、いくつかの重要な概念を理解するのに役立つ一般的に使用される用語の用語集で締めくくります。
簡単にまとめると、各アプローチの長所と短所の概要は次のとおりです。
アプローチ | データベース/プログラミングに焦点を当てた | 実践的な管理 | 抽象化のレベル | 複雑さのレベル |
---|---|---|---|---|
生のSQL | データベース指向 | 高 | なし | 低 |
クエリビルダー | 混合 | 低 | 低 | 低 |
ORM | プログラミング指向 | 低 | 高 | 高 |
生のSQLまたは別のデータベースネイティブクエリ言語を使用したデータの管理
>一部のアプリケーションは、データベースエンジンでサポートされている母国語を使用してクエリを記述および実行することにより、データベースと直接インターフェイスします。多くの場合、データベースインスタンスに接続、認証、および通信するために必要なのはデータベースドライバーだけです。
開発者は、接続を介してデータベースの母国語で記述されたクエリを送信できます。その見返りとして、データベースはクエリ結果をネイティブ形式の1つで提供します。多くのリレーショナルデータベースでは、選択するクエリ言語はSQLです。
ほとんどのリレーショナルデータベース、および一部の非リレーショナルデータベースは、強力なクエリを構築および実行するために、SQLとも呼ばれる構造化クエリ言語をサポートしています。 SQLは1970年代からデータの管理に使用されてきたため、十分にサポートされ、ある程度標準化されています。
ネイティブクエリの利点
SQLまたは別のデータベース母国語を使用すると、いくつかの明らかな利点があります。
1つの利点は、開発者がデータベースクエリを作成および管理し、結果を明示的に処理することです。これは多くの追加作業になる可能性がありますが、データベースが何を格納しているか、データをどのように表現しているか、後でデータを取得するときにそのデータをどのように提供するかという点で、驚くことはほとんどありません。抽象化が欠如しているということは、不確実性につながる可能性のある「可動部分」が少ないことを意味します。
この一例がパフォーマンスです。高度な抽象化レイヤーはプログラミングステートメントを変換することでSQLクエリを生成しますが、生成されたSQLは非常に非効率的です。不要な句、過度に広いクエリ、およびその他の事故により、データベース操作が遅くなり、脆弱でデバッグが困難になる可能性があります。 SQLでネイティブに記述することにより、ドメインの知識と常識をすべて活用して、多くのクラスのクエリの問題を回避できます
データベースネイティブクエリを使用するもう1つの理由は、柔軟性です。ネイティブデータベースのクエリ言語ほど柔軟な抽象化はありそうにありません。より高いレベルの抽象化は、2つの異なるパラダイム間のギャップを埋めようとします。これにより、表現できる操作のタイプが制限される可能性があります。ただし、生のSQLで記述する場合は、データベースエンジンのすべての機能を利用して、より複雑なクエリを表現できます。
ネイティブクエリの欠点
ネイティブクエリにはいくつかの明確な長所がありますが、問題がないわけではありません。
プレーンSQLを使用してアプリケーションからデータベースを操作する場合、有効なクエリを作成するには、基になるデータ構造を理解する必要があります。アプリケーションが採用するデータ型と構造、およびデータベースシステム内で利用可能な構造の間の変換については、完全に責任があります。
生のSQLを使用する際に留意すべきもう一つのことは、入力の安全性を管理するのは完全にあなた次第であるということです。これは、外部ユーザーから提供されたデータを保存している場合に特に当てはまります。この場合、特別に細工された入力により、データベースが許可するつもりのない情報を公開する可能性があります。
このタイプのエクスプロイトはSQLインジェクションと呼ばれ、ユーザー入力がデータベースの状態に影響を与える可能性がある場合は常に問題になる可能性があります。高度な抽象化ツールは、多くの場合、ユーザー入力を自動的にサニタイズし、このクラスの問題を回避するのに役立ちます。
ネイティブクエリ言語を使用するということは、ほとんどの場合、通常の文字列を使用してクエリを作成することを意味します。有効なクエリを作成するために入力をエスケープし、文字列を連結する必要がある場合、これは面倒なプロセスになる可能性があります。データベース操作は、誤ってデータを破壊する可能性が高い文字列操作の多くの層に巻き込まれる可能性があります。
ネイティブクエリの概要
このセクションでは主にSQLについて説明しましたが、ここでの情報のほとんどは、ネイティブデータベースクエリ言語にも同様に当てはまります。要約すると、生のSQLまたは同等のクエリ言語を直接使用すると、データベースがデータの保存と管理に使用する抽象化に最も近くなりますが、データを手動で管理するという面倒な作業をすべて行う必要があります。
クエリビルダーを使用したデータの管理
SQLなどのデータベースネイティブクエリ言語を使用する別のアプローチは、クエリビルダーと呼ばれるツールまたはライブラリを使用してデータベースと通信することです。
SQLクエリビルダーとは何ですか?
SQLクエリビルダーは、生のデータベースネイティブクエリ言語の上に抽象化レイヤーを追加します。これは、クエリパターンを形式化し、入力サニタイズを追加し、アイテムを自動的にエスケープしてアプリケーションに簡単に統合できるメソッドまたは関数を提供することで実現します。
SQLクエリビルダーを使用する場合、データベースレイヤーでサポートされる構造とアクションは依然としてかなり認識できます。これにより、データに比較的近い状態を保ちながら、プログラムでデータを操作できます。
通常、クエリビルダーは、メソッドまたは関数を使用してクエリに条件を追加するインターフェイスを提供します。メソッドをチェーン化することで、開発者はこれらの個々の「句」から完全なデータベースクエリを作成できます。
SQLクエリビルダーの利点
クエリビルダーはアプリケーションの他の部分と同じ構造(メソッドまたは関数)を使用するため、開発者は、文字列として記述された生のデータベースクエリよりも長期的な管理が容易であることに気付くことがよくあります。演算子とデータの違いを見分けるのは簡単で、クエリをクエリの特定の部分を処理する論理チャンクに分解するのも簡単です。
一部の開発者にとって、SQLクエリビルダーを使用するもう1つの利点は、基になるクエリ言語が常に非表示になるとは限らないことです。操作では文字列の代わりにメソッドを使用する場合がありますが、かなり透過的であるため、データベースに精通している人は操作の実行内容を簡単に理解できます。より高いレベルの抽象化を使用する場合、これは常に当てはまるとは限りません。
SQLクエリビルダーは、多くの場合、複数のデータバックエンドもサポートしており、たとえば、さまざまなリレーショナルデータベースの微妙な違いの一部を抽象化します。これにより、異なるデータベースを使用するプロジェクトに同じツールを使用できます。新しいデータベースへの移行が少し簡単になる場合もあります。
SQLクエリビルダーの欠点
SQLクエリビルダーには、ネイティブクエリ言語と同じ欠点がいくつかあります。
よくある批判の1つは、SQLクエリビルダーでは、データベースの構造と機能を理解して説明する必要があるということです。これは、一部の開発者にとって十分に役立つ抽象化ではありません。これは、クエリビルダー自体の特定の構文と機能に加えて、SQLをかなりよく理解している必要があることを意味します。
さらに、SQLクエリビルダーでは、取得するデータがアプリケーションデータにどのように関連するかを定義する必要があります。メモリ内のオブジェクトとデータベース内のオブジェクトの間には自動同期はありません。
クエリビルダーは、使用するように設計されたクエリ言語をエミュレートすることがよくありますが、抽象化レイヤーが追加されると、提供されたメソッドを使用して特定の操作ができない場合があります。通常、クエリをバックエンドに直接送信する「生の」モードがあり、クエリビルダーの一般的なインターフェイスをバイパスしますが、これは問題を解決するのではなく回避します。
SQLクエリビルダーの概要
全体として、SQLクエリビルダーは、データベースネイティブ言語を直接操作する際の主要な問題点のいくつかを具体的に対象とする抽象化レイヤーを提供します。 SQLクエリビルダーは、クエリのテンプレートシステムとしてほぼ機能し、開発者がデータベースを直接操作することと、抽象化レイヤーを追加することの間の境界線をたどることができます。
ORMを使用したデータの管理
抽象化階層のさらに上のステップはORMです。 ORMは通常、アプリケーションデータとより流動的に統合することを期待して、より完全な抽象化を目指しています。
ORMとは何ですか?
オブジェクトリレーショナルマッパー(ORM)は、リレーショナルデータベースのデータ表現とオブジェクト指向プログラミング(OOP)で使用されるメモリの表現との間の変換専用のソフトウェアです。 ORMは、データベース内のデータへのオブジェクト指向インターフェイスを提供し、使い慣れたプログラミングコンセプトを使用して、開発をスピードアップするために必要なボイラープレートコードの量を削減しようとします。
一般に、ORMは、オブジェクト指向のパラダイムを大幅に変更することなく、開発者がデータベースを操作できるようにするための抽象化レイヤーとして機能します。これは、データベースのストレージ形式の詳細に適応するための精神的な負荷を軽減するのに役立ちます。
特に、オブジェクト指向プログラミングのオブジェクトは、オブジェクト内の多くの状態をエンコードする傾向があり、継承や他のOOPの概念を通じて他のオブジェクトと複雑な関係を持つ可能性があります。この情報をテーブル指向のリレーショナルパラダイムに確実にマッピングすることは、多くの場合簡単ではなく、両方のシステムを十分に理解する必要があります。 ORMは、このマッピングの一部を自動化し、システム内のデータに表現力豊かなインターフェースを提供することで、この負担を軽減しようとします。
ORMの課題は、オブジェクト指向プログラミングに固有のものです。およびリレーショナルデータベース?
定義上、ORMは、オブジェクト指向アプリケーション言語とリレーショナルデータベース間のインターフェイスとして特別に設計されています。ただし、プログラミング言語内で使用されるデータ構造の抽象化とデータベースストアで使用されるデータ構造の抽象化の間でマッピングおよび変換を試みることは、抽象化が適切に調整されない場合に存在する可能性がある、より一般的な問題です。
プログラミングパラダイム(オブジェクト指向、機能、手続き型など)とデータベースタイプ(リレーショナル、ドキュメント、キー値など)に応じて、さまざまな量の抽象化が役立つ場合があります。多くの場合、アプリケーション内のデータ構造の複雑さにより、データストアとのインターフェースがいかに簡単であるかが決まります。
オブジェクト指向プログラミングは、説明しなければならない重要な状態と関係を持つ多くの構造を生成する傾向があります。他のいくつかのプログラミングパラダイムは、状態が格納される場所とその管理方法についてより明確です。たとえば、純粋関数型言語では可変状態が許可されていないため、状態は多くの場合、新しい状態を出力する関数またはオブジェクトの入力になります。このようにデータをアクションから明確に分離し、状態のライフサイクルを明確にすることで、データベースとのやり取りを簡素化できます。
いずれにせよ、2つの異なる表現間をマッピングするソフトウェアを介してデータベースとインターフェースするオプションがしばしば利用可能です。そのため、ORMはこれらの特定のサブセットを固有の課題で説明しますが、アプリケーションメモリと永続ストレージ間のマッピングでは、詳細に関係なく検討が必要になることがよくあります。
アクティブレコードとデータマッパーORM
異なるORMは、アプリケーションとデータベース構造の間でマッピングするために異なる戦略を採用しています。 2つの主要なカテゴリは、アクティブレコードパターンです。 およびデータマッパーパターン 。
アクティブレコードパターンは、コード内のオブジェクトの構造内にデータベースのデータをカプセル化しようとします。オブジェクトには、データベースを保存、更新、またはデータベースから削除するためのメソッドが含まれており、オブジェクトへの変更はデータベースに簡単に反映されるようになっています。一般に、アプリケーションのアクティブなレコードオブジェクトは、データベース内のレコードを表します。
アクティブレコードの実装では、コード内でクラスとインスタンスを作成して接続することにより、データベースを管理できます。これらは通常、クラスインスタンスをデータベースレコードに直接マップするため、コードで使用されているオブジェクトを理解していれば、データベースの内容を簡単に概念化できます。
残念ながら、これにはいくつかの大きな欠点もあります。アプリケーションはデータベースと非常に緊密に結合される傾向があるため、新しいデータベースに移行しようとしたり、コードをテストしたりするときに問題が発生する可能性があります。コードは、オブジェクトからオフロードされたギャップを埋めるためにデータベースに依存する傾向があります。これら2つのドメイン間の「魔法の」変換は、システムが複雑なオブジェクトを基盤となるデータ構造にシームレスにマッピングしようとするため、パフォーマンスの問題を引き起こす可能性もあります。
データマッパーパターンは、他の一般的なORMパターンです。アクティブレコードパターンと同様に、データマッパーはコードとデータベースの間の独立したレイヤーとして機能し、2つの間を仲介します。ただし、オブジェクトとデータベースレコードをシームレスに統合しようとするのではなく、それぞれを独立して存在させながら、オブジェクトとそれらの間を分離して変換しようとすることに重点を置いています。これは、マッピング、表現、シリアル化などを処理するデータベース関連の詳細からビジネスロジックを分離するのに役立ちます。
したがって、ORMシステムにオブジェクトとデータベーステーブル間のマッピング方法を理解させるのではなく、開発者は2つの間の明示的なマッピングを担当します。これにより、適切なマッピングを見つけるための作業が大幅に増える代わりに、密結合や舞台裏での操作を回避できます。
ORMのメリット
ORMは多くの理由で人気があります。
これらは、基盤となるデータドメインを、アプリケーションのコンテキスト内で簡単に推論できるものに抽象化するのに役立ちます。 ORMは、データストレージを独立したシステムと考えるのではなく、現在の作業の延長としてデータシステムにアクセスして管理するのに役立ちます。これにより、開発者は、ストレージバックエンドの微妙な違いにとらわれることなく、コアビジネスロジックでより迅速に作業できます。
これのもう1つの副作用は、ORMがデータベースとのインターフェースに必要な多くの定型文を削除することです。 ORMには、コードで行われた変更に基づいてデータベーススキーマの変更を管理するのに役立つ移行ツールが付属していることがよくあります。 ORMがデータベース構造への変更の管理に役立つ場合は、必ずしも完全なデータベーススキーマを事前に把握する必要はありません。多くの場合、アプリケーションとデータベースの変更は同じものであるか、密接に関連しているため、コードに変更を加えるときにデータベースへの変更を追跡するのに役立ちます。
ORMの欠点
ORMには欠陥がないわけではありません。多くの場合、これらはORMを有用にするのと同じ決定から生じます。
ORMの基本的な問題の1つは、データベースバックエンドの詳細を隠そうとする試みです。この難読化により、単純なケースや小規模な時間スケールでのORMの操作が容易になりますが、複雑さが増すにつれて、問題が発生することがよくあります。
抽象化が100%完全になることは決してなく、基礎となるクエリ言語またはデータベース構造を理解せずにORMを使用しようとすると、問題のある仮定につながることがよくあります。これにより、デバッグとパフォーマンスの調整が困難または不可能になる可能性があります。
おそらく、ORMを使用する際の最もよく知られている問題は、オブジェクト指向プログラミングとリレーショナルデータベースで使用されるリレーショナルパラダイムとの間の変換の難しさを説明するために使用される用語である、オブジェクトリレーショナルインピーダンスの不一致です。これら2つのカテゴリのテクノロジーで使用されるデータモデル間の非互換性は、複雑さが増すたびに、追加の不完全な抽象化が必要になることを意味します。オブジェクトと関係のインピーダンスの不一致は、時間の経過とともに複雑さが増し、成功または進路変更への道が困難または不可能になる状況につながる傾向があるため、(ベトナム戦争に関連して)コンピュータサイエンスのベトナムと呼ばれています。
一般に、ORMは、特に複雑なクエリの場合、代替手段よりも遅くなる傾向があります。 ORMは、他のケースを処理するのに十分な柔軟性が必要な一般的なパターンを採用しているため、比較的単純なデータベース操作に対して複雑なクエリを生成することがよくあります。あらゆる状況で正しいことを行うためにORMに依存することは、コストのかかるミスにつながる可能性があり、前もって追いつくのが難しい場合があります。
ORMの概要
ORMは、データベースの操作を非常に簡単にする便利な抽象化になります。これらは、設計と反復を迅速に行い、アプリケーションロジックとデータベース構造の概念的な違いを埋めるのに役立ちます。ただし、これらの利点の多くは両刃の剣として機能します。データベースを理解できなくなり、デバッグ、パラダイムの変更、またはパフォーマンスの向上が困難になる可能性があります。
用語集
データベースとアプリケーションの間のインターフェースとなるテクノロジーを使用する場合、なじみのない用語に遭遇する可能性があります。このセクションでは、遭遇する可能性のある最も一般的な用語のいくつかについて簡単に説明します。そのうちのいくつかはこの記事の前半で説明されており、いくつかはそうではありませんでした。
- データマッパー: データマッパーは、プログラミングデータ構造をデータベースに格納されているものにマッピングするデザインパターンまたはソフトウェアです。データマッパーは、2つのソース間の変更を同期させながら、それらを互いに独立させようとします。マッパー自体が、機能する翻訳を維持する責任があり、開発者はデータベースの表現を気にせずにアプリケーションのデータ構造を繰り返すことができます。
- データベースドライバー: データベースドライバは、アプリケーションとデータベース間の接続をカプセル化して有効にするように設計されたソフトウェアです。データベースドライバは、接続を確立および管理する方法の低レベルの詳細を抽象化し、データベースシステムに統合されたプログラムインターフェイスを提供します。通常、データベースドライバーは、開発者がデータベースと対話するために使用する最も低いレベルの抽象化であり、ドライバーによって提供される機能に基づいて構築されたより高いレベルのツールがあります。
- インジェクション攻撃: インジェクション攻撃は、悪意のあるユーザーが、ユーザー向けのアプリケーションフィールドで特別に細工された入力を使用して、不要なデータベース操作を実行しようとする攻撃です。多くの場合、これは、アクセスできないはずのデータを取得したり、データベース内の情報を削除または破壊したりするために使用されます。
- ORM: ORM、またはオブジェクトリレーショナルマッパーは、リレーショナルデータベースで使用されるデータ表現と、オブジェクト指向プログラミングで使用されるメモリ内の表現との間で変換を行う抽象化レイヤーです。 ORMは、データベース内のデータへのオブジェクト指向インターフェースを提供し、コードの量を減らし、使い慣れたアーキタイプを使用して開発をスピードアップしようとします。
- オブジェクトと相対インピーダンスの不一致: オブジェクトリレーショナルインピーダンスの不一致とは、オブジェクト指向アプリケーションとリレーショナルデータベース間の変換の難しさを指します。データ構造は大幅に異なるため、プログラムによるデータ構造をストレージバックエンドで使用される形式に忠実かつパフォーマンス的に変更および転記することは困難な場合があります。
- 永続性フレームワーク: 永続性フレームワークは、プログラムデータとデータベースの間のギャップを埋めるために開発されたミドルウェア抽象化レイヤーです。永続性フレームワークは、それらが使用する抽象化がオブジェクトをリレーショナルエンティティにマップする場合、ORMになることもあります。
- クエリビルダー: クエリビルダーは、使いやすさ、安全性、または柔軟性の機能を追加する制御されたインターフェイスを提供することにより、開発者がデータベースにアクセスして制御するのを支援する抽象化レイヤーです。通常、クエリビルダーは比較的軽量で、データアクセスとデータ表現の容易化に重点を置いており、データを特定のプログラミングパラダイムに変換しようとはしません。
- SQL: SQL(構造化照会言語)は、リレーショナルデータベース管理システムを管理するために開発されたドメイン固有言語です。これは、データベース内のデータとその組織構造を照会、定義、および操作するために使用できます。 SQLはリレーショナルデータベースに遍在しています。
結論
この記事では、アプリケーションからデータベースに接続するためのいくつかの異なるオプションについて説明しました。 SQLなどのデータベースネイティブクエリ言語を使用したり、クエリビルダーを使用してクエリを安全に作成したり、ORMを使用してより完全なレベルの抽象化を提供したりすることで、さまざまなレベルの抽象化と柔軟性を検証しました。
これらのアプローチにはそれぞれ用途があり、特定のタイプのアプリケーションに適しているものもあります。アプリケーション要件、組織のデータベース知識、および実装することを選択した抽象化(またはその欠如)のコストを理解することが重要です。全体として、各アプローチを理解することで、プロジェクトに最適なオプションを選択できる可能性が最も高くなります。