PerlのDBIx :: Classはいつ使用する必要がありますか?


17

DBIx :: Classは、DBIを介して接続できるデータベースへの人気のあるPerlインターフェースです。技術的な詳細については適切なドキュメントがありますが、その適切な使用に関する情報はほとんどありません(おそらくそれを望まない状況を含む)。

多くの場合、人々はデータベースに関係するすべてのものに使用すべきだと考えているため、再帰的にそれを利用します。しかし、私はそれが苦痛のポイントになるほど誤用されるのをよく見ました。コードとアーキテクチャのレビュー中の私の質問は、常に「Fooがあなたに与える利益は何ですか?」です。ほとんどの場合、これらの状況で私が見る開発者は、それに対する一貫した答えを形成できません。しかし、その後、彼らはしばしば単純なSQLを理解しません。

過去2か月間、「なぜDBIx :: Classを使用するのですか?」と人々に尋ねてきました。良い回答を1つだけ受け取りました(そして、その人はフォローアップの「いつそれを使わないのか」にも答えることができました)。リード開発者のPeter Rabbitsonは、FLOSS Weeklyのインタビューで答えに近づきましたが、インタビューの途中で少し埋もれてしまいました。

それでは、DBIx :: Classを使用することがプロジェクトに適しているかどうかをどのように判断できますか?


3
別の本を書いていますか?:)
シンバク

1
私の経験では、DBICが過剰に使用されている状況でDBICを使用すると痛みが生じます。そして、非常に強力であるにもかかわらず、人々は基本的な機能(SQL生成と結合)のみを使用し、他に何も必要としないため、しばしばやり過ぎです。それが、DBIx :: Liteを書いた理由です。DBIx:: Liteは、これらの基本的な機能を提供し、スキーマをハードコーディングする必要はありません。
アレッサンドロ

回答:


24

質問に答える前に、ある程度の背景が整っていると思います。

問題の核心

長年にわたって開発者にインタビューして採用した後、2つのことを学びました。

  1. 開発者の大半は、データベース設計の経験がほとんどありません。

  2. データベースを理解していない人と、ORMを嫌う人との間には、ゆるやかな相関関係があることに気付きました。

(注:はい、そうです、データベースを非常によく理解し、ORMを嫌う人がいることは知っています)

外部キーが重要である理由、itemテーブルにメーカー名を埋め込んでいない理由customer.address1、または、、、customer.address2およびcustomer.address3フィールドがデータベースのバグを書きやすくするためにORMを追加するのは良い考えではない理由を人々が理解していないとき何の助けにもなりません。

代わりに、適切に設計されたデータベースとOLTPユースケースを使用すると、ORMは黄金になります。面倒な作業のほとんどはなくなり、DBIx :: Class :: Schema :: Loaderなどのツールを使用すると、優れたデータベーススキーマからPerlコードを数分で動作するようになります。私はパレート規則を引用し、私の問題の80%が作業の20%で解決されたと言いますが、実際には、それ以上のメリットがあると思います。

ソリューションの乱用

一部の人々がORMを嫌うもう1つの理由は、抽象化が漏れるからです。MVC Webアプリの一般的なケースを考えてみましょう。ここによく見られるものがあります(擬似コード):

GET '/countries/offices/$company' => sub {
    my ( $app, $company_slug ) = @_;
    my $company = $app->model('Company')->find({ slug => $company_slug }) 
      or $app->redirect('/');
    my $countries = $app->model('Countries')->search(
     {
         'company.company_id' => $company->company_id,
     },
     {
         join     => [ offices => 'company' ],
         order_by => 'me.name',
     },
   );
   $app->stash({
     company   => $company,
     countries => $country,
   });
}

人々はそのようなコントローラールートを書いて、自分自身を後ろで軽くたたいて、それは良い、きれいなコードだと思っています。彼らはコントローラーでSQLをハードコーディングすることにat然としますが、異なるSQL構文を公開する以上のことはしていません。ORMコードをモデルにプッシュダウンする必要があり、それを実行できます。

GET '/countries/offices/$company' => sub {
   my ( $app, $company_slug ) = @_;
   my $result = $app->model('Company')->countries($company_slug)
     or $app->redirect('/');
   $app->stash({ result => $result });
}

今何が起こっているか知っていますか?モデルを適切にカプセル化し、ORMを公開していません。後で、データベースの代わりにキャッシュからそのデータをフェッチできることがわかった場合、コントローラーコードを変更する必要はありません(そして簡単です)テストを作成し、ロジックを再利用します)。

実際には、コントローラー(およびビュー)全体でORMコードがリークされ、スケーラビリティの問題が発生すると、アーキテクチャではなくORMのせいになります。ORMは悪いラップを取得します(多くのクライアントでこれを繰り返し見ます)。代わりに、抽象化を非表示にして、ORMの制限に本当に達したときに、コードをORMに緊密に結合して独り占めするのではなく、問題に適したソリューションを選択できるようにします。

レポートおよびその他の制限

Rob Kinyonが上記で明らかにしたように、報告はORMの弱点になる傾向があります。これは、複雑なSQLまたは複数のテーブルにまたがるSQLがORMでうまく機能しない場合がある、より大きな問題のサブセットです。たとえば、ORMが私が望まない結合タイプを強制することがあり、それを修正する方法がわからないことがあります。または、MySQLでインデックスヒント使用したいのですが、簡単ではありません。または、SQLが非常に複雑であるため、提供されている抽象化よりもSQLを記述する方が良い場合もあります。

これは、私がDBIx :: Class :: Reportを書き始めた理由の一部です。これまでのところ、それはうまく機能し、人々がここで抱えている問題の大部分を解決します(読み取り専用インターフェースで問題がなければ)。松葉杖のように見えますが、実際には、抽象化を漏らさない限り(前のセクションで説明したとおり)、作業がDBIx::Classさらに簡単になります。

それで、いつDBIx :: Classを選択するのでしょうか?

私にとっては、ほとんどの場合、データベースへのインターフェイスが必要な場合に選択します。私は何年も使用しています。ただし、OLAPシステムには選択しないかもしれませんし、新しいプログラマーは確かにそれと格闘しています。また、メタプログラミングが必要であることがよくありDBIx::Classますが、ツールは提供されていますが、ドキュメント化が非常に不十分です。

DBIx::Class正しく使用するための鍵は、ほとんどのORMと同じです。

  1. 抽象化を漏らさないでください。

  2. ひどいテストを書いてください。

  3. 必要に応じて、SQLにドロップダウンする方法を知ってください。

  4. データベースを正規化する方法を学びます。

DBIx::Class、一度学習すれば、ほとんどの面倒な作業はあなたに代わって処理され、アプリケーションをすばやく作成するのが簡単になります。


1
使用しない場合のために、別のリストを追加できます。:)
ブライアンd foy

1
これは、(私が年を過ごした、と言う多くの読者には自明ではない、おそらくあなたに明らかにされているが、#dbix-class#catalyst) - 「抽象化が漏れていない」ビットの鍵は、あなたがDBICにして作業しているすべての単一のものであるということですCookieカッターの動作を提供する何かのサブクラス。サブクラスにメソッドを追加することを強くお勧めします。Q&Dジョブを実行していない限り、作成したメソッドのみがパブリックインターフェイスの一部である必要があります。
ホッブズ

@hobbs:確かに、それは人々がこれで最も間違っていると思う場所であり、DBICで立ち往生する方法です。私たちは、多くの場合、人々は自分が小さなことをしていることを知っていると思いますが、大部分は知らないことを見つけます。
ブライアンd foy

9

いつ何かを使用するかを知るには、その目的が何であるかを理解することが重要です。製品の目標は何ですか。

DBIx :: ClassはORM-オブジェクトリレーショナルマッパーです。ORMは、リレーショナル/セットベースのリレーショナルデータベースデータ構造を取得し、それをオブジェクトツリーにマップします。従来のマッピングは、テーブルの構造をクラスの説明として使用して、行ごとに1つのオブジェクトです。データベース内の親子関係は、オブジェクト間のコンテナ関係として扱われます。

それが骨です。しかし、それはORMを使用すべきかどうかを判断するのに役立ちません。ORMは、主に次のことが当てはまる場合に役立ちます。

  • リレーショナルデータベースを使用します。
  • データは主にOLTP(オンライントランザクション処理)に使用されます。
  • アプリケーション内でレポートを作成しません。

ORMの最大の強みは、優れたSQLを構築して、リレーショナルデータ構造の上にオーバーレイされたツリーグラフを歩くことです。SQLは多くの場合複雑で複雑ですが、インピーダンスの不整合を管理する代償です。

ORMは行抽出SQLの作成には非常に優れていますが、照合SQLの作成には非常に適していません。これは、レポートが作成されるSQLの種類です。この種の照合は、ORMではなく、さまざまなツールを使用して構築されます。

さまざまな言語の多くのORMがあり、Perlのものもあります。他のマッパーはClass :: DBIとRose :: DBです。DBIx :: Classは、多くの場合、結果セットの強度が他のクラスより優れていると考えられています。これは、SQL生成がSQL実行から分離される概念です。


更新:Ovidへの応答として、DBIx :: Class(SQL :: Abstractを使用)は、返す列と使用するインデックスヒントの両方を指定する機能を提供します。

ただし、一般的に、これを実行したい場合は、その特定のクエリにORMを使用しない方が良いでしょう。要確認-ORMの主な目的は、テーブルの行を、属性がテーブルの列であるクラスのオブジェクトにマップすることです。一部の属性のみを設定している場合、そのオブジェクトの潜在的なユーザーは、どの属性が設定されているかを知りません。これは恐ろしい防御的なプログラミングやORMに対する一般的な憎悪につながります。

ほとんどの場合、インデックスヒントを使用したり、返される列を制限したりすることは、速度の最適化やクエリの集約です。

  • 集約クエリは、ORMが設計されていないユースケースです。DBIx :: Classは集計クエリを作成できますが、オブジェクトグラフは作成しないので、DBIを直接使用してください。
  • クエリの対象となるデータは、アクセス方法に関係なく、基になるデータベースには大きすぎるため、パフォーマンスの最適化が使用されます。ほとんどのリレーショナルデータベースは、ほとんどのデータとインデックスがRAMに収まるSSDから実行される最大1〜3 MM行のテーブルに最適です。状況がこれよりも大きい場合、すべてのリレーショナルデータベースに問題が発生します。

はい。優れたDBAは、OracleまたはSQL * Serverで100MM行以上のテーブルを作成できます。これを読んでいるのであれば、スタッフに優れたDBAはいません。

以上のことから、優れたORMはオブジェクトグラフを作成するだけではなく、データベースの内省可能な定義も提供します。これを使用して、オブジェクトグラフを作成せずに、アドホッククエリを作成し、DBIの場合と同様にそれらを使用できます。


私が目にするほとんどすべてがレポートを書いていると思うので、おそらく人々は手動のクエリに頼らざるを得ないのです(そしてそれが苦痛です)。
ブライアンd foy

1
レポートの手動クエリにドロップダウンする必要がある理由がわかりません。DBICを使用して、かなり複雑なレポートを作成しました。もちろん、多くの場合、「プリフェッチ」と「結合」を多用して、カスタマイズされた大規模な結果セットを作成します。
デイブクロス

Dave:手動SQLの方がはるかに簡単に記述でき、3つのテーブルから必要な7つのフィールドだけを引き出して、それらを1行で表すことができます。さらに、生のSQLを記述するときにヒントを提供するのがはるかに簡単です。
カーティスポー

>必要な7つのフィールドのみをプルするようにするには、はい、それがResultSet検索の「列」属性の用途です。生のSQLの実行に関して聞いた、または見た唯一の有効な引数は、次のとおりです。本当に構築されていません。3.インデックスヒントをハッキングしようとしています。繰り返しますが、それはRDBMSの弱点の多くですが、時にはそれを行う必要があります。そして、そのような機能をDBICに追加するだけで可能です。
ブレンダンバード

8

Interchange6 e-commerceプラットフォーム(および主要なスキーマチェーンソー)の中心的な開発者の1人として、DBICについてかなり深い経験を持っています。以下は、このプラットフォームを非常に優れたプラットフォームにしているものです。

  • クエリジェネレーターを使用すると、多くのデータベースエンジン(およびそれぞれの複数のバージョン)に対して1回書き込むことができます。現在、MySQL、PostgreSQL、SQLiteでInterchange6をサポートしています。時間とリソースが得られたら、さらに多くのエンジンのサポートを追加します。現在、エンジン全体の違いに対応するための追加のコードを含むプロジェクト全体のコードパスは2つだけであり、これは純粋に特定のデータベース関数の不足(SQLiteが不足しているため)または変更されたMySQLのイディオシーによるものです。 LEAST関数が2つのマイナーバージョン間でNULLを処理する方法。

  • 事前定義されたクエリは、アプリケーションコードから(引数の有無にかかわらず)呼び出すことができる単純なメソッドを構築できることを意味するため、アプリケーションコードを散らかすのではなく、ほとんどスキーマ定義内にクエリを保持します。

  • 構成可能なクエリの生成により、クエリを小さな理解可能な事前定義されたクエリに分割し、単一のステップで構築された場合、DBIC(純粋なSQLではさらに悪い)で長期間維持するのが困難な複雑なクエリを作成するために一緒に連鎖できます。

  • Schema :: Loaderにより、DBICをレガシーアプリケーションで使用できるようになり、新しい生命のリースと将来へのはるかに単純なパスが提供されます。

  • DBICプラグイン、DeploymentHandler、およびMigrationはすべて、私の生活をよりシンプルにするツールのセットに非常に多くを追加します。

DBICと他のほとんどのORM / ORMのようなプラットフォームとの大きな違いの1つは、物事のやり方を案内しようとするものの、好きなクレイジーなことをやめさせないことです。

  • クエリでキーとして関数名を指定するだけで、DBICが知らないSQL関数とストアドプロシージャを使用できます(MySQLでLEASTを使用しようとすると、楽しいことがありますが^ DBICのせいではありません) 。

  • リテラルSQLは、何かを行うための「DBICの方法」がなく、返される結果がアクセサを備えた素敵なクラスにラップされている場合に使用できます。

TL; DRいくつかのテーブルを含む本当にシンプルなアプリケーションに使用することはおそらくないでしょうが、より複雑なものを管理する必要がある場合、特にエンジン間の互換性と長期的な保守性が重要な場合、DBICは一般的に私の好みのパス。


7

(開始する前に、これはDBIC、DBI、MojoベースのDBIラッパーを比較するだけです。他のPerl ORMの経験はないので、直接コメントしません)。

DBICは多くのことを非常にうまく行います。私はヘビーユーザーではありませんが、理論は知っています。それはSQL生成の非常に素晴らしい仕事をし、特に(私が言われたように)結合などを処理します。また、他の関連データのプリフェッチを非常にうまく行うことができます。

私が見ている主な利点は、結果をモデルクラスとして直接使用できることです。これは、「結果セットメソッドの追加」とも呼ばれ、結果を取得し、それらの結果に対してメソッドを呼び出すことができます。一般的な例は、DBICからユーザーオブジェクトを取得してから、メソッドを呼び出してパスワードが有効かどうかを確認することです。

確かにスキーマの展開は困難な場合がありますが、常に困難です。DBICには、独自のスキーマを手作業で管理するよりも簡単で、おそらく簡単になるツール(外部モジュールの一部)があります。

コインの反対側には、モジョ風味のDBIラッパーなど、他の感性に訴える他のツールがあります。これらは無駄のない、まだ使用可能なという魅力を持っています。また、ほとんどがMojo :: Pg(オリジナル)からヒントを得て、フラットファイルのスキーマ管理やpubsub統合などの便利な機能を追加しています。

これらのMojoフレーバーモジュールは、DBICのもう1つの弱点、つまり非同期クエリを(まだ)実行できないという点から生まれました。著者は、技術的にはおそらくさらに迅速に可能であることを保証しましたが、適切なAPIの設計には問題があります。(確かに私はこれを手伝うように頼まれさえしました、そして私はそうするでしょうが、私はそれに専念しなければならない時間に針を動かす方法を知りません)。

TL; DRは、SQLが好きな場合や非同期が必要な場合を除き、DBICを使用します。非同期の場合は、Mojoフレーバー付きDBIラッパーを調べてください。


6

3年前にDBICとDBIでこれについての考えを書きました。要約すると、2つの主な理由を挙げました。

  1. DBICは、私が作成するほとんどすべてのアプリケーションに必要な些細なSQLのすべてについて考える必要がないことを意味します。
  2. DBICは、ダムデータ構造ではなく、データベースからオブジェクトを返します。これは、私がプレイする標準的なオブジェクト指向の良さをすべて持っていることを意味します。特に、DBICオブジェクトにメソッドを追加できると本当に便利だと思います。

アンチパターンに関しては、私が考えることができるのはパフォーマンスだけです。CPUからすべてのクロックサイクルを本当に圧縮したい場合は、おそらくDBICはこのジョブに適したツールではありません。しかし、間違いなく、作成するアプリケーションにとって、これらのケースはますますまれになっています。データベースと対話し、DBICを使用しなかった新しいアプリケーションを最後に作成したことを思い出せません。もちろん、DBICが生成するクエリの調整について少し知っていると役立ちます。


2
ええと、十分な文字(「righ ttool」)を変更しないため、タイプミスを修正できません。不思議なことにラメ。しかし、これは私を困惑させるような答えです。PerlHacksの投稿で、Robが指摘していることの1つに取り組んでいると思いますが、他のことは考慮しないでください。多くの場合、手動SQLに戻る人がいます。
ブライアンd foy

1

スケーリング方法:

  1. DBIソケットコンストラクターとテストメソッドを提供する1つのクラスを作成します。

  2. そのクラスをSQLクエリクラス(SQLテーブルごとに1つのクラス)に派生し、コンストラクター時にソケットをテストします。

  3. クラススコープ変数を使用して、テーブル名とプライマリインデックス列名を保持します。

  4. SQLで静的に定義するのではなく、これらの変数からすべてのSQL補間テーブル名とプライマリインデックス列を記述します。

  5. エディターマクロを使用して、sqlステートメントのみを入力しながら、基本的なDBIメソッドのペア(準備と実行)を作成できるようにします。

それができれば、比較的簡単に1日中、DBIの上にきれいなAPIコードを書くことができます。

あなたが見つけることは、クエリの多くが複数のテーブル間で移植可能であるということです。その時点で、EXPORTERクラスに切り取って貼り付け、必要な場所にそれらを振りかけることができます。これは、テーブル名とプライマリインデックス列名のクラススコープ内挿が作用する場所です。

このアプローチを使用して、比較的良好なパフォーマンスで数百のDBIメソッドに拡張しました。DBIコードを他の方法で管理しようとは思わないでしょう。

DBIを使用する場合:常に。

それはあなたの本当の質問ではなかったと思います。あなたの本当の質問は、「これは巨大なPITAのように見えます。これをする必要はないことを教えてください」

あなたはしません。正しくレイアウトすると、DBI部分は、ほとんど自動化できるほど冗長になります。


あなたが共有できるオープンソースプロジェクトを持っている可能性はありますか、それとも各クラスの例でgithubの要点だけですか?あなたが言っているアイデアは興味深いものであり、おそらく多くの人々のプロジェクトにとって実行可能なものになると思いますが、いくつかの例を使って始めるのは少し簡単でしょう。
msouth

0

私はPerlの専門家ではありませんが、よく使用しています。私にはわからないことや、もっとうまくやる方法がわからないことがたくさんあります。ドキュメンテーションにも関わらず、まだ理解できないものもあります。

「ああ、これは単純なプロジェクトです。ORMの肥大化は必要なく、セットアップモジュールとスキーマモジュールに煩わされたくない」と思うので、DBIから始める傾向があります。しかし、非常に迅速に-ほぼ毎回-私はすぐにその決定のために自分自身を呪い始めました。SQLクエリ(比較プレースホルダだけでなく動的クエリ)で創造性を発揮したいときは、DBIを使用して健全性を維持するのに苦労します。SQL :: Abstractは非常に役立ちますが、通常はそれで十分でしょう。しかし、私の次の精神的闘争は、コード内に大量のSQLを維持することです。linesいヒアドキュメント内に行と埋め込みSQLの行があると、非常に気が散ります。高品質のIDEを使用する必要があるかもしれません。

結局、私は何度もDBIに固執しています。しかし、もっと良い方法があればいいのにと思っています。DBIx :: Classにはいくつかの本当にすてきな特性があり、私はそれを数回使用しましたが、それは最大のプロジェクトを除くすべてのプロジェクトにとって非常にやり過ぎです。管理が面倒なのは、分散SQLを使用したDBIまたは分散スキーマモジュールを使用したDBICです。

(ああ、制約やトリガーのようなものはDBICにとって私にとって大きなプラスです。)


4
この答えはあまりうまくいきません-確かに、DBIxを使用すると問題が発生しますが、なぜそれらの問題があるのですか?DBIは柔軟性がなく、DBと緊密に結合していませんか、スケーラブルではありませんか?
ジェイエルストン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.