シェア

はじめに

アプリケーションデータを管理するためにデータベースを使用することは、データ永続化のための最も一般的な選択肢の1つです。データベースは、高速な情報ストレージと検索を可能にし、データ整合性の保証を提供し、個々のアプリケーションインスタンスの寿命を超えた永続性を提供します。プロジェクトの要件と好みに合わせて、無数の種類のデータベースが利用可能です

ただし、アプリケーションから直接データベースを操作することは必ずしも容易ではありません。データ構造の表現方法の違いは、しばしば課題につながります。異なるエンティティ間の関係に関する微妙な点を表現することの難しさも、問題を引き起こす可能性があります。これに対処するために、コアアプリケーションとデータ層間のインターフェースとして機能するのを助けるために、多くの異なるツールが作成されてきました。

このガイドでは、3つの一般的なアプローチ、生のSQL、クエリビルダー、ORM(オブジェクト・リレーショナル・マッパー)の間で生じるいくつかの違いを見ていきます。各アプローチの利点と欠点を比較し、最後に、主要な概念を理解するのに役立つ一般的な用語集で締めくくります。

簡略化された概要として、以下は各アプローチの長所と短所の一般的な見解です。

アプローチデータベース/プログラミング重視ハンズオン管理抽象化のレベル複雑さのレベル
生のSQLデータベース指向高いなし低い
クエリビルダー混合低い低い低い
ORMプログラミング指向低い高い高い

生のSQLまたは別のデータベースネイティブクエリ言語を使用したデータ管理

一部のアプリケーションは、データベースエンジンがサポートするネイティブ言語を使用してクエリを作成および実行することにより、データベースと直接インターフェースします。多くの場合、データベースインスタンスに接続、認証、および通信するために必要なのはデータベースドライバだけです。

開発者は、データベースのネイティブ言語で記述されたクエリを接続を介して送信できます。見返りに、データベースはクエリ結果もネイティブ形式の1つで提供します。多くのリレーショナルデータベースでは、選択されるクエリ言語はSQLです。

ほとんどのリレーショナルデータベースだけでなく、一部の非リレーショナルデータベースも、強力なクエリを構築および実行するために、構造化クエリ言語(SQLとも呼ばれます)をサポートしています。SQLは1970年代からデータの管理に使用されているため、十分にサポートされており、ある程度標準化されています。

ネイティブクエリの利点

SQLまたは別のデータベースネイティブ言語を使用することには、いくつかの明確な利点があります。

1つの利点は、開発者がデータベースクエリを記述および管理し、結果を明示的に処理することです。これは追加の作業が多くなる可能性がありますが、データベースが何を格納しているか、データをどのように表現しているか、後で取得されたときにそのデータをどのように提供するかという点で、驚きが少ないことを意味します。抽象化の欠如は、不確実性につながる可能性のある「可動部分」が少ないことを意味します。

この一例はパフォーマンスです。高度な抽象化レイヤーはプログラミングステートメントを翻訳してSQLクエリを生成しますが、生成されたSQLは非常に非効率になる可能性があります。不要な句、過度に広範なクエリ、およびその他のミスは、脆弱でデバッグが困難な低速なデータベース操作につながる可能性があります。ネイティブにSQLで記述することにより、ドメイン知識と常識をすべて活用して、多くの種類のクエリの問題を回避できます。

データベースネイティブクエリを使用するもう1つの理由は、柔軟性です。ネイティブデータベースクエリ言語ほど柔軟性のある抽象化はおそらくありません。より高いレベルの抽象化は、2つの異なるパラダイム間のギャップを埋めようと試み、それらが表現できる操作の種類を制限する可能性があります。ただし、生のSQLで記述する場合、データベースエンジンのすべての機能を利用して、より複雑なクエリを表現できます。

ネイティブクエリの欠点

ネイティブクエリには明確な強みがいくつかありますが、問題がないわけではありません。

プレーンSQLを使用してアプリケーションからデータベースと対話する場合、有効なクエリを作成するには、基盤となるデータ構造を理解する必要があります。アプリケーションが採用するデータ型と構造、およびデータベースシステム内で利用可能な構造の間を翻訳する責任は完全にあなたにあります。

生のSQLを扱うときに留意すべきもう1つの点は、入力の安全性を管理するのは完全にあなた次第であるということです。これは、外部ユーザーから提供されたデータを保存する場合に特に当てはまります。特別に作成された入力により、データベースが許可する予定のなかった情報を公開する可能性があります。

このタイプのエクスプロイトはSQLインジェクションと呼ばれ、ユーザー入力がデータベースの状態に影響を与える可能性がある場合は常に潜在的な問題です。より高い抽象化ツールは、多くの場合、ユーザー入力を自動的にサニタイズし、このクラスの問題を回避するのに役立ちます。

ネイティブクエリ言語を使用するということは、ほとんどの場合、通常の文字列でクエリを作成することを意味します。これは、入力をエスケープし、文字列を連結して有効なクエリを作成する必要がある場合に、面倒なプロセスになる可能性があります。データベース操作は、誤ってデータを破損させる可能性の高い、多くの文字列操作のレイヤーに包まれる可能性があります。

ネイティブクエリの概要

このセクションでは主にSQLについて説明しましたが、ここにある情報のほとんどは、同等のネイティブデータベースクエリ言語にも同様に当てはまります。要約すると、生のSQLまたは同等のクエリ言語を直接使用すると、データベースがデータを格納および管理するために使用する抽象化に最も近づきますが、データを手動で管理するという大変な作業をすべて行う必要があります。

クエリビルダーを使用したデータ管理

SQLなどのデータベースネイティブクエリ言語を使用する代わりに、クエリビルダーと呼ばれるツールまたはライブラリを使用してデータベースと通信する方法があります。

SQLクエリビルダーとは?

SQLクエリビルダーは、生のデータベースネイティブクエリ言語の上に抽象化レイヤーを追加します。これを行うために、クエリパターンを形式化し、入力サニテーションを追加し、アプリケーションへの統合を容易にするためにアイテムを自動的にエスケープするメソッドまたは関数を提供します。

SQLクエリビルダーを使用する場合でも、データベース層でサポートされている構造とアクションはかなり認識可能です。これにより、データに比較的近い状態を維持しながら、プログラムでデータを操作できます。

通常、クエリビルダーは、メソッドまたは関数を使用してクエリに条件を追加するインターフェースを提供します。メソッドを連鎖させることで、開発者はこれらの個々の「句」から完全なデータベースクエリを作成できます。

SQLクエリビルダーの利点

クエリビルダーはアプリケーションの残りの部分と同じ構造(メソッドまたは関数)を使用するため、開発者は文字列として記述された生のデータベースクエリよりも長期的に管理しやすいと感じることがよくあります。演算子とデータの違いを簡単に区別でき、クエリの特定の部分を処理する論理的なチャンクにクエリを簡単に分解できます。

一部の開発者にとって、SQLクエリビルダーを使用するもう1つの利点は、基盤となるクエリ言語を常に隠蔽するとは限らないことです。操作は文字列の代わりにメソッドを使用する可能性がありますが、非常に透過的である可能性があり、データベースに精通している人が操作が何をするかを理解しやすくなります。より高度な抽象化を使用する場合、これは必ずしも当てはまりません。

SQLクエリビルダーは、多くの場合、複数のデータバックエンドもサポートしており、たとえば、さまざまなリレーショナルデータベースの微妙な違いを抽象化しています。これにより、異なるデータベースを使用するプロジェクトで同じツールを使用できます。新しいデータベースへの移行がわずかに容易になる可能性さえあります。

SQLクエリビルダーの欠点

SQLクエリビルダーは、ネイティブクエリ言語と同じ欠点がいくつかあります。

一般的な批判の1つは、SQLクエリビルダーでも、データベースの構造と機能を理解して考慮する必要があることです。これは、一部の開発者にとっては十分に役立つ抽象化ではありません。これは、クエリビルダー自体の特定の構文と機能に加えて、SQLをかなりよく理解している必要があることを意味します。

さらに、SQLクエリビルダーでは、取得するデータがアプリケーションデータにどのように関連するかを定義する必要があります。インメモリオブジェクトとデータベース内のオブジェクトの間には自動同期はありません。

クエリビルダーは、多くの場合、連携するように設計されたクエリ言語をエミュレートしますが、抽象化の追加レイヤーは、提供されたメソッドを使用すると特定の操作が不可能になる場合があることを意味する可能性があります。通常、バックエンドにクエリを直接送信するための「raw」モードがあり、クエリビルダーの一般的なインターフェースをバイパスしますが、これは問題を解決するのではなく、回避します。

SQLクエリビルダーの概要

全体として、SQLクエリビルダーは、データベースネイティブ言語を直接操作する際の主要な苦痛のいくつかを特に対象とした、薄い抽象化レイヤーを提供します。SQLクエリビルダーは、クエリのテンプレートシステムとしてほぼ機能し、開発者がデータベースを直接操作することと、抽象化の追加レイヤーを追加することの間を歩むことを可能にします。

ORMを使用したデータ管理

抽象化階層のさらに上のステップはORMです。ORMは一般に、アプリケーションデータとのより流動的な統合を期待して、より完全な抽象化を目指しています。

ORMとは?

オブジェクト・リレーショナル・マッパー(ORM)は、リレーショナルデータベースのデータ表現とオブジェクト指向プログラミング(OOP)で使用されるメモリ内の表現の間を変換することに特化したソフトウェアの一部です。ORMは、データベース内のデータへのオブジェクト指向インターフェースを提供し、開発をスピードアップするために必要なボイラープレートコードの量を減らし、使い慣れたプログラミング概念を使用しようとします。

一般に、ORMは、開発者がオブジェクト指向パラダイムを大幅に変更することなくデータベースを操作するのを支援することを目的とした抽象化レイヤーとして機能します。これは、データベースのストレージ形式の詳細に適応する精神的な負荷を軽減するのに役立ちます。

特に、オブジェクト指向プログラミングのオブジェクトは、オブジェクト内に多くの状態をエンコードする傾向があり、継承やその他のOOP概念を通じて他のオブジェクトと複雑な関係を持つことができます。この情報をテーブル指向のリレーショナルパラダイムに確実にマッピングすることは、多くの場合、簡単ではなく、両方のシステムをよく理解する必要があります。ORMは、このマッピングの一部を自動化し、システム内のデータへの表現力豊かなインターフェースを提供することにより、この負担を軽減しようとします。

ORMの課題はオブジェクト指向プログラミングとリレーショナルデータベースに固有のものですか?

定義上、ORMは、オブジェクト指向アプリケーション言語とリレーショナルデータベース間のインターフェースとなるように特別に設計されています。ただし、プログラミング言語内で使用されるデータ構造の抽象化と、データベースストアで使用されるデータ構造の抽象化の間をマッピングおよび変換しようとすることは、抽象化がきれいに整列しない場合にいつでも存在する可能性のある、より一般的な問題です。

プログラミングパラダイム(オブジェクト指向、関数型、手続き型など)とデータベースタイプ(リレーショナル、ドキュメント、キーバリューなど)に応じて、さまざまな量の抽象化が役立つ場合があります。多くの場合、アプリケーション内のデータ構造の複雑さが、データストアとのインターフェースをどれだけ簡単にするかを決定します。

オブジェクト指向プログラミングは、考慮する必要のある重要な状態と関係を持つ多くの構造を生成する傾向があります。他のいくつかのプログラミングパラダイムは、状態がどこに格納され、どのように管理されるかについてより明示的です。たとえば、純粋関数型言語は可変状態を許可しないため、状態は多くの場合、新しい状態を出力する関数またはオブジェクトの入力になります。データとアクションのこの明確な分離、および状態ライフサイクルの明示性は、データベースとの対話を簡素化するのに役立ちます。

いずれにせよ、2つの異なる表現の間をマッピングするソフトウェアを介してデータベースとインターフェースするオプションは、多くの場合利用可能です。したがって、ORMは独自の課題を持つこれらの特定のサブセットを記述しますが、アプリケーションメモリと永続ストレージ間のマッピングは、詳細に関係なく考慮が必要になることがよくあります。

アクティブレコード vs データマッパーORM

異なるORMは、アプリケーションとデータベースの構造間のマッピングに異なる戦略を採用しています。2つの主要なカテゴリは、アクティブレコードパターンデータマッパーパターンです。

アクティブレコードパターンは、データベースのデータをコード内のオブジェクトの構造内にカプセル化しようとします。オブジェクトには、データベースから保存、更新、または削除するためのメソッドが含まれており、オブジェクトへの変更はデータベースに簡単に反映されるようになっています。一般に、アプリケーションのアクティブレコードオブジェクトは、データベース内のレコードを表します。

アクティブレコードの実装を使用すると、コード内でクラスとインスタンスを作成および接続することにより、データベースを管理できます。これらは通常、クラスインスタンスをデータベースレコードに直接マッピングするため、コードで使用されているオブジェクトを理解していれば、データベース内のものを概念化するのは簡単です。

残念ながら、これはいくつかの大きな欠点も伴う可能性があります。アプリケーションはデータベースと非常に密結合する傾向があり、新しいデータベースに移行しようとしたり、コードをテストしたりするときに問題が発生する可能性があります。コードは、オブジェクトからオフロードされたギャップを埋めるためにデータベースに依存する傾向があります。これらの2つのドメイン間の「魔法の」変換は、システムが複雑なオブジェクトを基盤となるデータ構造にシームレスにマッピングしようとするため、パフォーマンスの問題につながる可能性もあります。

データマッパーパターンは、もう1つの一般的なORMパターンです。アクティブレコードパターンと同様に、データマッパーは、コードとデータベースの間で仲介する独立したレイヤーとして機能しようとします。ただし、オブジェクトとデータベースレコードをシームレスに統合しようとする代わりに、それぞれを独立して存在させながら、それらの間の分離と変換を試みることに焦点を当てています。これは、マッピング、表現、シリアル化などを扱うデータベース関連の詳細からビジネスロジックを分離するのに役立ちます。

したがって、ORMシステムにオブジェクトとデータベーステーブル間のマッピング方法を考えさせるのではなく、開発者は2つの間の明示的なマッピングを担当します。これは、適切なマッピングを理解するのに大幅に多くの労力がかかる代わりに、密結合と舞台裏の操作を回避するのに役立ちます。

ORMの利点

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を使用することによって提供されるさまざまなレベルの抽象化と柔軟性を検討しました。

これらの各アプローチにはそれぞれの用途があり、他のアプローチよりも特定のタイプのアプリケーションに適している場合があります。アプリケーションの要件、組織のデータベース知識、および実装を選択する抽象化(またはその欠如)のコストを理解することが重要です。全体として、各アプローチを理解することで、プロジェクトに最適なオプションを選択できる可能性が最も高くなります。

FAQ

ビジュアルSQLクエリビルダーは、SQLクエリを作成するためのグラフィカルユーザーインターフェイスです。

通常、クエリビルダーは比較的軽量で、データアクセスとデータ表現の容易化に重点を置いており、データを特定のプログラミングパラダイムに変換しようとはしません。

オンラインSQLクエリビルダーは、SQLの書き方を知らなくてもSQLクエリを迅速に構築するのに役立つクラウドベースのツールです。

通常、クエリビルダーは比較的軽量で、データアクセスとデータ表現の容易化に重点を置いており、データを特定のプログラミングパラダイムに変換しようとはしません。

アクティブレコードパターンは、データベースのデータをコード内のオブジェクトの構造内にカプセル化しようとします。オブジェクトには、データベースから保存、更新、または削除するためのメソッドが含まれており、オブジェクトへの変更はデータベースに簡単に反映されるようになっています。

アクティブレコードの実装により、コード内でクラスとインスタンスを作成および接続することでデータベースを管理できます。

データマッパーパターンは、コードとデータベースの間で、両者間を仲介する独立したレイヤーとして機能しようとします。

各々を独立して存在させながら、それらの間の分離と翻訳を試みることに重点を置いています。これは、マッピング、表現、シリアライゼーションなどを扱うデータベース関連の詳細からビジネスロジックを分離するのに役立ちます。

ネイティブクエリは、データベースクライアントを使用してデータベースで直接実行できるSQLステートメントで構成されるデータベースクエリです。

著者について
Daniel Norman

Daniel Norman

Danielは、最新のWebおよびクラウド環境で幅広い経験を持つソフトウェアエンジニアです。彼は15年以上にわたってデータベースを使用した開発に携わっており、オープンソースと最新の開発ツールに情熱を注いでいます。
Justin Ellingwood

Justin Ellingwood

Justinは、2013年からデータベース、Linux、インフラストラクチャ、および開発ツールについて執筆しています。彼は現在、妻と2羽のウサギと一緒にベルリンに住んでいます。彼は通常、三人称で書く必要はありません。それは関係者全員にとって安心です。