問題の声明
多くのシステムでは、さまざまな目的のために、特定のフィールドの値としてデータベースからオブジェクトを選択することがしばしば必要です。 たとえば、リストから商品のサプライヤを選択します。 これは、システムユーザーインターフェイスの一部です。
ただし、ユーザーに加えて、システムにはプログラマー、チューニングエンジニア、デザイナーなどがいて、どのオブジェクトを選択するかをシステムに伝える必要がある場合もあります。 たとえば、サプライヤの都市を選択した後、サプライヤのリストを制限する必要がある場合があります。 これを行うために、プログラマーは通常、「SELECT id、name FROM agents WHERE city =?」などの適切なSQLを作成します。 つまり、SQLを使用して、データベースから選択するオブジェクトをシステムに通知します。
ほとんどの場合、この選択はシステムのソースコードの一部です。 PHPまたはJavaコード(または、より良いのは、SQLバンドルまたはストアドプロシージャ)に隠されているため、このようなコードはおなじみのようです。 ただし、フィルターはソースコードの一部ではなく、XSLT / HTMLコードのシステムエンジニアまたはレイアウトデザイナーによって生成されるシステム構成の一部になる場合があります。
例1:エンタープライズ管理システム。 「Employee」オブジェクトのリストと「Office」オブジェクトのリストがあります。 「従業員」オブジェクトにオフィス選択フィールドを追加し、従業員が登録されている同じ国のオフィスのリストを表示する必要があります。 通常の方法は、クラス「Employee」の属性「Office of the employee」のプロパティに対応するSQLフィルターを記述することです。 このSQLは、たとえば、従業員IDを入力として受け取り、対応するオフィスのリストを計算します。
例2:XSLTベースのコンテンツ管理システム。 データベース(記事など)からオブジェクトが選択され、XMLが構築され、XSLT変換を使用してHTMLが取得され、クライアントに渡されます。 ただし、多くの場合、記事自体のテキストに加えて、追加の要素をXMLに添付する必要があります。たとえば、クイックナビゲーション用のセクションにある他の記事のリストです。 または、現在のサイトのセクションのリストであっても、デザインにより制御システムを介して変更できる場合。 適切なオブジェクトを選択するには、同じSQLを使用して、現在のオブジェクト(記事)のIDを引数として使用します。
このような例でSQLを使用すると、次の欠点があります。
1)Java / PHP / .NETに精通していないシステムエンジニアは、SQLの知識が必要です。
2)エンジニアはシステムテーブルの内部構造を知っている必要があります
3)このようなアプリケーション設定にSQLを使用すると、テーブル構造が「フリーズ」され、パブリックAPIの一部になります
4)このSQLには、対応するテーブルから少なくともすべてのデータを読み取る権利があります。 行レベルのセキュリティを使用せずにアプリケーションの権利を制限することは非現実的です。
したがって、Arp.Siteでは、テンプレートテキストからオブジェクトを選択するために異なる指定方法が使用されます。 XSLTは主にテンプレートに使用されるため、XMLに最も近いテクノロジーを使用するのが最も論理的でした。 つまり、XPath。
XPathを使用すると、 述語の制約を考慮して、 ツリー (より正確には、非循環指向のグラフ)からオブジェクトを選択できます。 XPathでの式の記述は、SQLよりもコンパクトで理解しやすいものです。
例1:
//[@=$/@]
例2:
//folder[@active='true']/article
同じセクションから記事を選択します。
//site[@active='true']/folder
現在のサイトのセクションを選択します。
技術的な実装
実装には、解釈とコンパイルという2つのアプローチを使用できます。
解釈とは、オブジェクトの実(非常に小さなサイト)または仮想(大規模システム)DOMツリーの構築と、XPath式自体のXPathエンジンおよびDOMツリーのルート(または現在のオブジェクト)への「フィード」です。 実行の結果は、DOMツリーの別のオブジェクト(またはNodeList)へのポインターであり、システムIDのセットに変換されます。
利点:
-「ゼロから」実装する最速の方法
-加速を目的とした最適化がない場合、最も理解しやすい
欠点
-ツリー内の各遷移操作がデータベースへのアクセスを必要とする場合、最も抑制的な方法
-実行の最適化を行うことは非常に困難です。
最適化とは何ですか、なぜ必要なのですか? 例:
-XPath式の一部は、たとえば
//article[@name='123']
です。 XPathプロセッサはオブジェクトのツリー全体を調べ、それぞれのタイプ(記事)を比較し、 nameプロパティの存在と '123'の値との同等性を調べます。 最適化の存在により、このようなすべての操作を、たとえば、名前が「123」に等しいすべての記事を選択する1つの操作に置き換えることができます。
-XPath式
//*[@active='true']
は、「現在の」オブジェクトのリスト(サイト、セクション、サブセクション、記事(ナビゲーションパスの作成用))を選択する必要があります。 ただし、ツリー内のすべてのオブジェクトを列挙することは望ましくないため、これらの式も最適化する必要があります。
Arp.Siteは、解釈にApache Commons JXPathライブラリを使用します。 このライブラリは、オブジェクト(ビン、DOMノード、JDOMノードなど)、またはこのオブジェクトからプロパティと子オブジェクトを取得する方法をライブラリに説明する場合は任意のオブジェクトを受け入れます。 また、このライブラリを使用すると、いくつかの標準ハンドラを独自のものに置き換えることができます。 たとえば、Axis = DESCENDANT_OR_SELF(XPathの操作 "//")のStepハンドラーを独自の最適化されたハンドラーに置き換えます。
情報ツリーのオブジェクトに接続されたオブジェクトは、ライブラリ入力に送られます。 必要に応じて、その子孫に関する情報がデータベースからロードされます。
SQLでのコンパイル
2番目のアプローチは、XPathをSQLにプリコンパイルし、その後XPathをキャッシュして将来実行することでした。
メリット
-データベースに対するSQLクエリの一意性による大幅な高速化
欠点
-コードを記述するのははるかに難しく、使用されているすべてのテーブルエイリアスを追跡し、値を返し、さまざまな述語を正しく処理する必要があります。
-SQLの作成作業が文字列の作業になります
-SQLを簡素化および高速化するには、多数の最適化が必要です
最適化の例。 XPATH
//site[@active='true']/folder
。 最適化を行わないと、SQLは次のようになります。
SELECT f.id FROM objects f, objects s WHERE s.id=f.parent AND s.class='site' AND f.class='folder' AND s.id IN (1,4,73,423)
(ここで、 4.73.423-「現在の」オブジェクトのリスト)。
ただし、システムは次のヒントを使用できます。
-オブジェクトツリーのサイトをサイトの子孫にすることはできません(つまり、現在のオブジェクトのリストにあるのは1つだけです)
-サイトは間違いなくデータベースにあります
-彼のIDは「1」です
SQLは次のように書き換えることができます。
SELECT f.id FROM objects f WHERE f.parent=1 f.class='folder'
そのような最適化は多数ありますが、それらのほとんどはシステム固有です。
SQLコンパイルの例:
-
//site[@ active='true']/*[@active='true']/*[@state='published']
SELECT t7.id FROM struct_cells t7, registry_objects t8 WHERE t7.obj=t8.id AND t7.erased=0 AND t8.erased=0 AND t8.state=2 AND t7.state=3 AND t7.parent=360965
-
//*[@current='true']/*[@state='published'] | //*[@current='true']/*[@state='published']/file[@state='published' or @state='archived']
(SELECT t4.id FROM struct_cells t4, registry_objects t5 WHERE t4.obj=t5.id AND t4.erased=0 AND t5.erased=0 AND t5.state=2 AND t4.state=3 AND t4.parent=1) UNION (SELECT t13.id FROM struct_cells t13, registry_objects t14, struct_cells t10, registry_objects t11 WHERE t13.obj=t14.id AND t13.erased=0 AND t14.erased=0 AND t14.class=10 AND (t14.state=2 AND t13.state IN (3,4)) AND t10.obj=t11.id AND t10.erased=0 AND t11.erased=0 AND t11.state=2 AND t10.state=3 AND t10.parent=1 AND t13.parent=t10.id))
JPAQLでのコンパイル
CriteriaQuery APIを含むJPA 2.0の登場後に開発された3番目のアプローチは、クエリ文字列を直接操作せずに、対応するAPIを使用してクエリを構築することです。
メリット
-クエリ構築コードの大幅な簡素化
-タイプミスを取り除く
-JPAでサポートされているすべてのデータベースでコードが機能します
欠点
-EJBQLを簡素化および高速化するには、多数の最適化が必要です
-EJBQLトランスレータコードが最適化されていない可能性があるため、わずかにスローダウン-> SQL
-EJBQLには存在しないUNIONなどの関数をエミュレートする必要があるため、大幅な速度低下。
-CriteriaQueryのlimit / row_numberなどの一部の操作がサポートされていないため、
//site/folder[2]
などの位置述語をサポートできません。
JPAQLのコンパイル例:
-
//site[@active='true']/*[@active='true']/*[@state='published']
SELECT t0.id
FROM ru.arptek.arpsite.content.Cell as t1, ru.arptek.arpsite.content.Cell as t0
INNER JOIN t0.webObject as t2
WHERE ( t1.parent.id=360930 ) and ( t1.erased=0 ) and ( t1.id in (360965, 360930, 417026, 124316, 63316, 1) ) and ( t1.id=t0.parent.id ) and ( t0.erased=0 ) and ( t2.erased=0 ) and ( ( t2.stateId=2 ) and ( t0.stateId=3 ) )
-
//*[@current='true']/*[@state='published'] | //*[@current='true']/*[@state='published']/file[@state='published' or @state='archived']
SELECT t0.id
FROM ru.arptek.arpsite.content.Cell as t0
WHERE ( exists (
SELECT 1
FROM ru.arptek.arpsite.content.Cell as t1
INNER JOIN t1.webObject as t2
WHERE ( t1.parent.id=1 ) and ( t1.erased=0 ) and ( t2.erased=0 ) and ( ( t2.stateId=2 ) and ( t1.stateId=3 ) ) and ( t0=t1 )) ) or ( exists (
SELECT 1
FROM ru.arptek.arpsite.content.Cell as t3, ru.arptek.arpsite.content.Cell as t4
INNER JOIN t3.webObject as t5
INNER JOIN t4.webObject as t6
WHERE ( t4.parent.id=1 ) and ( t4.erased=0 ) and ( t6.erased=0 ) and ( ( t6.stateId=2 ) and ( t4.stateId=3 ) ) and ( t4.id=t3.parent.id ) and ( t3.erased=0 ) and ( t5.erased=0 ) and ( t5.objectClassId=10 ) and ( ( t5.stateId=2 ) and ( t3.stateId in (3, 4) ) ) and ( t0=t3 )) )
法的通知
このアイデアは2002年からArp.Siteで使用されており、技術的な変更(解釈、SQLでのコンパイル、JPAQLでのコンパイル)のみを受けています。 私は他の雇用者とのこのアイデアの実施と直接的な関係はありません(アドバイスは手伝いましたが)ので、このトピックの内容は関連するNDAには該当しません。 従来技術が利用可能であるため、この技術は2002年以降の特許では保護できません。 初期の特許の存在は調査されていません。
最初の実装の作成者はTimofei Sysoevです。SQLおよびJPAQLのコンパイルテクノロジの導入は、謙虚な僕です。