1

レガシーSQLステートメントのセクションが依存関係に基づいているいくつかのインスタンスがあります。例えば。

if (x !=null)
{
  SQL = "SELECT z WHERE x > y";
}
else
{
  SQL = "SELECT z WHERE x <= y";
} 

SQL2 = SQL + " JOIN a ON b";

このレガシー コードから PreparedStatements を作成しています。ここでのベストプラクティスは何ですか。var SQL 用の PreparedStatement を作成し、それを SQL2 内にネストする必要がありますか? ネストせずに SQL2 に基づく複数の PreparedStatement が存在する必要がありますか、それともまったく異なるものですか?

SQL var は多くの長く複雑な SQL クエリ内で再利用されるため、コードは例よりもはるかに複雑です。

編集: プロジェクトの設計では PreparedStatements を使用する必要があります。現時点では、ライブラリを使用する選択肢はありません。

4

6 に答える 6

4

> var SQL の PreparedStatement を作成し、SQL2 内にネストする必要がありますか?

いいえ

>または、ネストなしで SQL2 に基づく複数の PreparedStatement が存在する必要があります

はい

さらに:クエリごとに1つの文字列を作成できれば、より良いでしょう。SQL とコードを混在させるのはあまり好きではありません。デバッグと理解が難しくなり、簡単にテストするために SQL ツールにコピー/貼り付けすることはできません。コードから SQL を分離することで、クエリを操作 (実際の fetch ) から分離し、保守が容易になります。さらに、コードがあなたのものでない場合は、理解がはるかに簡単になります。

文字列を繰り返しているように見えても問題ありません。ポイントは、ステートメントをできるだけ単純化することです。

私はこのようなことをします:

final class DatabaseQueries {
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y JOIN A, B ";
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y JOIN A, B";
 }

そして、あなたのクラスからそれを使用します:

 PreparedStatement pstmt = getCon().prepareStatement( getQuery() );


 private String getQuery() { 
     if( x != null ) { 
          return DatabaseQueries.SOME_SCENARIO;
     } else { 
           return DatabaseQueries.SOME_OTHER_SCENARIO;
     }
 }

クラス「DatabaseQueries」を作成しているときに、多くの文字列を繰り返していることがわかります。一部の部分を他の定数に置き換えても問題ないと思います。

final class DataBaseQueries { 
    // this one is private
    private final static String JOIN_A_B = " join A, B ";
    public final static String SOME_SCENARIO       = "SELECT z WHERE x > y " + JOIN_A_B ;
    public final static String SOME_OTHER_SCENARIO = "SELECT z WHERE x <= y " + JOIN_A_B ;

}

ここでのポイントは、物事を単純化することです。これが最初のステップです。2 番目のステップでは、非常に複雑なクエリを作成するためのクラスを作成できますが、おそらく YAGNI.

クエリが多すぎる場合は、この 質問のように ResourceBundle からロードするように置き換えることができます

これが役立つことを願っています。

于 2008-12-22T22:37:07.723 に答える
2

Ibatisはこれが得意です。

<select id="queryName" parameterClass="com.blah.X"><!<[CDATA[
  SELECT z
  FROM a
  JOIN b ON a.id = b.foreign_key
  WHERE

  <isNotNull property="value">
    x > y
  </isNotNull>

  <isNull property="value">
    x <= y
  </isNull>

]]></select>

これは、Ibatis ができることのごく一部ですが、非常に軽量です。優れた技術。

于 2008-12-22T22:33:41.600 に答える
1

これは、準備済みステートメント パラメーターの適切な使用法ではありません。パラメータは、SQL 式のリテラル値の代わりにのみ使用できます。テーブル名、列名、またはその他の SQL 構文ではありません。

SQLクエリの一部を構築するためにいくつかのライブラリを使用できます。私はPHPでこのようなライブラリに取り組みましたZend_Db_Select.

編集: Java用の同様のライブラリを少しグーグルで検索したところ、役立つ可能性のあるこのオプションが見つかりました:

  • Squiggleは、SQL SELECT ステートメントを動的に生成するための小さな Java ライブラリです。[その] スイート スポットは、実行時に変化する条件を使用して複雑なクエリを構築する必要があるアプリケーション向けです。通常、この文字列を作成する方法を理解するのは非常に困難です。Squiggle は、この痛みの多くを取り除きます。

これは無料で、非常に柔軟なオープンソース ライセンスである Apache License の下で提供されます。

「java query builder」をグーグルで検索すると、他にも多くのオプションが見つかりましたが、一部は無料ではありませんでした。一部は、プログラムによるクエリ ビルダーではなく、視覚的なクエリ ビルダーでした。

もう 1 つのオプションは、Hibernate のような複雑なオブジェクト リレーショナル マッピング フレームワークを使用することですが、これは現在のタスクにはやり過ぎのようです。

于 2008-12-22T22:17:16.907 に答える
0

ここに同様の質問があります

簡単な答え - 最善の方法はありません。次のような結果になる可能性があります。

String selectQuery =
  (new SelectQuery())
  .addColumns(t1Col1, t1Col2, t2Col1)
  .addJoin(SelectQuery.JoinType.INNER_JOIN, joinOfT1AndT2)
  .addOrderings(t1Col1)
  .validate().toString();

しかし、私にとってはさらに悪いことです。

于 2008-12-22T22:31:19.250 に答える
0

一方で、純粋なオブジェクト アプローチがあると思いますが、レガシー コードを理解しようとしている場合、これはおそらくあまり役​​に立ちません。私は、「完璧」を目指すよりも、本当に厄介なレガシー コードをリファクタリングする際に、アプリ全体を一度に書き直さなくてもできるように、モジュール化され、十分に文書化されているように、小さな部分を単純化する方が、多くの場合、より良く、より簡単であることに気付きました。悪いコードをリファクタリングする際の最大のハードルは、あまりにも大きなステップを踏むと、何も壊れていないという確信が持てなくなることです。また、未定義または文書化されていない動作が存在する可能性があります。

少なくとも、最初のパスとしてロジックと SQL を分割します。あなたが望まないことは次のようなものです:

String sql = "yadda yadda yadda ? yadda yadda WHERE ";
if (mystery condition 1){
   sql = sql + " page=?"
}
else if (mystery condition 2)
{
 sql = sql + " ORDER BY ? "
}

しばらくすると、どのステートメントが構築されているかを知ることができなくなります。最初のSQLを少し複製することを節約する価値はありません。次のように理解しやすいかもしれません:

private static final String FIND_PAGE_QUERY = "...."
private static final String ORDER_BY_QUERY =" ..."

if (mystery condition 1){
   return process(FIND_PAGE_QUERY, parameters);
}
else if (mystery condition 2)
{
  return process(ORDER_BY_QUERY, parameters);
}

次に、 Spring JDBC Row Mappersなどのように渡されたクエリとパラメーターをラップするものを作成するだけです 。これはまだ醜いですが、得られたものからの増分ステップとして行うのは簡単で、少なくともクエリ生成の混乱の一部を整理します.

于 2008-12-22T22:32:16.627 に答える
0

もう 1 つのアプローチは、条件を SQL ステートメント自体に移動して、1 つのステートメントだけで済むようにすることです。次のようになります。

SELECT z WHERE (? IS NOT NULL AND x > y) OR (? IS NULL AND x <= y)

次に、パラメーターに適切な値をバインドします。

それが最善のアプローチかどうかはわかりません...人々はコードがあまり明確でないと感じるかもしれません。しかし、それは可能性です。

于 2008-12-23T14:32:07.453 に答える