3

その場で HQL クエリにパッチを適用し、結合と where パーツを動的に追加する HQL ベースのエンジンを作成する必要があります (質問を防ぐために、基準 API ではなく HQL を使用する必要があります)。

from Object aたとえば、次のようなHQLに注入しますfrom Object a JOIN a.path b WHERE b.id='XYZ'

いくつかのオプションが表示されますが、どちらも機能しません。

  1. JavaString.insert()アプローチ。WHERE ステートメントの位置を見つけて、ステートメントの前に結合を追加し、ステートメントの後に他の部分を追加します。このような HQL の簡単な作業ではありません

    SELECT a, (SELECT b FROM Object2 b WHERE b.path=a) FROM オブジェクト a WHERE EXISTS(SELECT 1 FROM ... WHERE)

保留中のアルゴリズム: 各 WHERE の前に括弧 () の数を計算でき、それがゼロの場合 - 正しい WHERE 位置が見つかりました。Java でのより単純なアルゴリズムまたは単純な実装を提案してくれる人はいますか?

2.私のタスクは正規表現で解決できると思いますが、正しい正規表現を書くことができませんString.replace()

3. AST/ANTLR ベースの文法を見て、HQL を解析できましたが、それがどのように役立つかわかりません (つまり、ステートメントを取得しましたが、正しいステートメントの位置を取得できませんでしたWHERE)。

4. スタンドアロン ライブラリは SQL 解析用に存在しますが、HQL 用には存在しません。

とにかく、考えてくれてありがとう:)

4

2 に答える 2

2

あなたが提案したオプションについて、いくつかの考えを以下に示します。

  1. String.insert手動解析あり

    a) 開始する HQL 文法のサブセットを簡単に定義できる場合、b) 文字単位で解析できると確信している場合、および c) 文字列を手動で解析することが合理的な方法である可能性があります。サブセットの文法はめったに、またはまったく変更されないことを知っています。次のようなアプローチに従う必要があるようです。

    • ストリームから文字を読み取ります。
    • それが文字列の先頭である場合 (例: a ')、文字列読み取りアプローチを開始して、'('')'が通常の入力として処理されないようにします。文字列の終わりの場合は、通常の処理を再開します。
    • グループ化された式 (例: a '(') の先頭である場合は、新しい式などの追跡を開始します。
    • の場合は' '、消費されたばかりの単語を見て、それをどうするかを決定します。それが式の始まり (例: SELECT) である場合は、ID (例: a) を確認し、次の一連の可能性を確認し、それらを処理するなど。
    • 次の文字を読み、手順を繰り返します。

    一般に、このアプローチは最初から正しく理解するのが難しく、ルールが追加または削除された場合に修正するのが難しいように思えます。問題を解決するためにこのアプローチは避けますが、入力を汚れのように単純に保つことができる場合は、頼りになる適切な解決策です。

  2. 正規表現

    このアプローチは最初のアプローチよりもクリーンですが、正規表現の制限に完全に制約されますSELECT a, (SELECT b ...) FROM Object a ...。正規表現が得意とする線形データやフラット データではなく、構造化データ ( ) を分析しようとしているのです。それでも、ステップ 1 のような手動のプロセスと組み合わせるとうまくいくかもしれませんが、難しそうに思えます。

  3. HQL文法を使用したANTLR

    標準の Hibernate ダウンロードには、HQL を読み取るパーサーを生成するために ANTLR が使用する HQL 文法ファイルが含まれています。これを HQL/Hibernate コードから分離し (文法にはマイナーに見える依存関係がいくつかあります)、対処することがわかっているものに切り詰めることができます。これにより、前の 2 つのオプションに比べて 2 つの大きな利点が得られます。最初から完全に機能するパーサーがあり、公式の HQL 入力と同じように、またはほとんど解析しないようにコード化されます。プログラミングが含まれます。

    このオプションについて私が考えることができる 3 つの欠点があります: HQL 文法はバージョン ANTLR 2.7 を使用します。更新するのが簡単かどうかはわかりませんが、私の ANTLR 3 の経験と文法を調べると、両方のバージョンの知識が必要になります。しかし、もちろん、最新バージョンの ANTLR を使用する義務はありません。したがって、最新バージョンにしたい場合、これはマイナス面にすぎません。

    2 番目の欠点は、文法を他の HQL コードから分離する行為であり、それらの依存関係が浮かび上がらないようにすることです。必要のないルールを削除することもできますが、それは非常に簡単なプロセスです。役に立たない/使えないものを切り取るのはとても簡単なので、ここで難しいことは何もありません。

    3 つ目の欠点は、パーサー コードを生成し、自信を持って変更を加えるために、基本的な ANTLR を学習する必要があることです。ANTLR の基本は簡単に習得でき、必要な場合はここで十分なサポートがあると思います。そのため、以前の欠点と同様に、これはかなりマイナーだと思います。

    全体として、このアプローチは非常に良いスタートだと思います。必要な文法を無料で取得し、その文法から必要なパーサーを無料で取得します。必要なのは、文法を Hibernate プロジェクトから切り離し、初心者の ANTLR の知識をいくらか用意することだけです。

  4. その他 スタンドアロン

    あなたの問題を解決するこのカテゴリに該当するプロジェクトは思いつきません。

  5. 独自の文法を使用した ANTLR

    オプション 3 の代替としてこのオプションを追加しました。開始する HQL のサブセットを知っていて、それを表す一般的な文法 (必ずしも ANTLR ではない) に自信がある場合は、独自の ANTLR 文法をゼロから記述して、そこからパーサーを生成します。縮小するのではなく構築するため、オプション 3 よりも手間がかかりますが、ANTLR について詳しく学ぶにつれて、このアプローチに慣れる可能性があります。

一般的な ANTLR ソリューションの場合、(生成された) ANTLR パーサーによって生成された AST ツリーを分析することで、JOINand句を配置する場所を見つけることができます(この Bart Kiers の回答は、ANTLR によって生成された単純なツリーの例を示していますパーサー)。ANTLR のみのソリューションについて話し合いたい場合は、ここにリストされている他のオプションと絡み合わないように、新しい質問を開始することをお勧めします。WHERE

あなたは難しい問題に取り組んでいるようで、詳細はわかりませんが、ソリューションでANTLRを使用すると、事前に時間を節約でき(手書きのパーサーを作成する必要はありません)、将来の変更に備えることができます必要です (文法を変更してパーサーを再生成するだけです)。オプション 3 またはオプション 5 のうち、快適さのレベルに適した方をお勧めします。

于 2012-10-21T07:16:44.853 に答える
0

パス#1をたどりました。思ったほど複雑ではありませんでした。

コードの期待値pFindPartは、「WHERE」または「FROM」の形式である必要があります。

private int findInsertPosition(StringBuilder pStringBuilder, String pFindPart){
    String HQL = pStringBuilder.toString().toUpperCase(Locale.US);
    int whereIndex = HQL.length();
    int findPartLength = pFindPart.length();
    while(whereIndex >= 0){
        whereIndex = HQL.lastIndexOf(pFindPart, whereIndex);
        if (whereIndex >=0){
            String rightPart = HQL.substring(whereIndex + findPartLength);
            int count = 0;
            for(char c : rightPart.toCharArray()){
                switch(c){
                    case ')': count--; break;
                    case '(': count++; break;
                }
            }
            if (count == 0) break;
            whereIndex--;
        }
    }
    return whereIndex;
}
于 2012-10-31T14:37:21.417 に答える