2010-11-24 7 views
7

アプリケーションにSQLクエリを配置する最適な場所は何ですか?一般に、アプリケーションにSQLクエリを配置する場所はどこですか?

クエリが大きく、書式設定が必要な場合があります。

StringBuilderを使用してクエリを追加すると、非常に乱雑に見えます。

ファイルにそれらを保存し、それらの要求が行われるたびに読んで - 。悪いアイデアのように見える(しかし、私は、ファイルからの読み込みは、静的なブロックに入れることができると思います)

+0

ストアそれらのビューとしてSQLデータベース自体にストアドプロシージャを格納します。 – onedaywhen

答えて

12

あなたは、クラスのロード時定数に読み出さリソースファイルにSQLクエリを保持:

private static final String PERSON_QUERY; 

static{ 
    InputStream str = null; 
    try{ 
     str = ThisClass.class.getResourceAsStream("/path/to/query.sql"); 
     PERSON_QUERY = IOUtils.toString(str); 
    }catch(IOException e){ 
     throw new IllegalStateException("Failed to read SQL query", e); 
    }finally{ 
     IOUtils.closeQuitely(str); 
    } 

} 

あなたがSQLを編集するために、お気に入りのエディタを使用することができますこの方法を、しかし、あなたはまだでクエリを取得しますjava内の定数

あなたはこれに多くを行う場合は、ヘルパーメソッドにコードを抽出します。

public static String loadResourceToString(final String path){ 
    final InputStream stream = 
     Thread 
      .currentThread() 
      .getContextClassLoader() 
      .getResourceAsStream(path); 
    try{ 
     return IOUtils.toString(stream); 
    } catch(final IOException e){ 
     throw new IllegalStateException(e); 
    } finally{ 
     IOUtils.closeQuietly(stream); 
    } 
} 

とあなたの静的なブロックにすることを使用します。私の意見では

private static final String PERSON_QUERY; 
private static final String ADDRESS_QUERY; 
private static final String AGE_QUERY; 

static{ 
    PERSON_QUERY = Helper.loadResourceToString("queries/personQuery.sql"); 
    ADDRESS_QUERY = Helper.loadResourceToString("queries/addressQuery.sql"); 
    AGE_QUERY = Helper.loadResourceToString("queries/ageQuery.sql"); 
} 

、異なる言語は常にあるべき分離された。 SQL、HTML、XML、JavaScriptなどをJavaコードからアセンブルするのはひどい習慣です。可能であれば、プレーンテンプレートやVelocityのようなテンプレートエンジンを使用してください。これは、多くの利点をもたらします。その1つは、Javaクラスを再コンパイルせずにテンプレートを変更できることです。

PS:私は上記のコードでApache Commons/IOを使用していますが、これは必ずしも簡単ではありません。

+0

すでにIOUtilsを使用している場合は、ほとんどのコードを** FileUtils.readFileToString(新しいファイル(パス)); ** – kolobok

+0

@akapelkoに置き換えるほうがずっと簡単です。しかし、私のコードは、ファイルとJARの両方のエントリで動作します。 –

+0

私のために働く!すばらしいです! –

2

PreparedStatement

を読みますクエリ のように、すべての可変部分を保存する必要はありません。この中

insert into table_x values (?,?,?);

statement.setString(1,"hello");statement.setInt(2,1);を使用して、statement.setDouble (3,4.555);

、最後にstatement.execute();を入力することができます。

PS:プリペアドステートメント文字列をプロパティファイルに保存することをお勧めします。

+2

問題は "insert into table_x ..."クエリを配置する場所です。特に質問が20行の場合。 – HanuAthena

1

ええ、その良いこと。プロパティファイルは悪い考えではありません。しかし、時々我々は即座にクエリを構築する必要があります。そのためにはStringBuilerというアプローチが適しています。

+1

@Ansari:書式設定されたSQLファイルは、読みやすさのために保持する必要がある20-30行になる場合があります。だから、それらをプロパティファイルに入れることは役に立たないかもしれません。 – HanuAthena

+0

@ハヌアエナ:どうしてですか?プロパティの値を複数の行に書くことができます。そして私はそれが読みやすさを損なうとは思わない。とにかく、水平スクロールを避けるために、コード内で同じことをするでしょう。 –

+0

@HanuAthena:今、「ダリオ」が例を思いついた。今私のポイントを見ることができれば幸いです。 –

1

.propertiesファイルに入れることができます。設定にApache Commonsを使用すると、avoid reading files every timeを使用できます。

あなたはこのルートで行くことを選択した場合は、バックスラッシュを使用して複数の行に1つのクエリ解体によってreadibilityを支援することができます:私のシナリオでは

myLongQuery: select col1, col2, col3, col4 from \ 
      table1 where \ 
      col1 = 'something' 
0

を、私はすべてのSQLクエリがある特定のDAOを持っていますブロックはstatic finalに登録されています。

例:

public class MySQLUserDAO extends UserDAO { 

    private static final String SQL_COUNT = "SELECT COUNT(1) AS TOTAL FROM USER"; 
// private static final String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS, DOB) VALUES (?, ?, ?, ?, ?)"; 
    private static final String SQL_DELETE = "DELETE FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_RETRIEVE = "SELECT * FROM USER WHERE USER_ID = ?"; 
    private static final String SQL_UPDATE = "UPDATE USER SET FIRST_NAME = ?, MIDDLE_NAME = ?, LAST_NAME = ?, GENDER = ?, EMAIL_ADDRESS = ?, DOB = ? WHERE USER_ID = ?"; 
    private static final String SQL_FIND_EMAIL = "SELECT * FROM USER WHERE EMAIL_ADDRESS = ?"; 
    private static final String SQL_FIND_FIRST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_FIRST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(FIRST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_LAST_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) = LOWER(?)"; 
    private static final String SQL_FIND_LAST_NAME_LIKE = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(LAST_NAME))) LIKE ?"; 
    private static final String SQL_FIND_BY_NAME = "SELECT * FROM USER WHERE LTRIM(RTRIM(LOWER(CONCAT_WS(' ', FIRST_NAME, LAST_NAME)))) LIKE ?"; 

しかし、動的ステートメントの作成を必要とクエリのため、私はそれがために使用されている方法でそれを置きます。

例:

/* (non-Javadoc) 
    * @see net.imatri.dao.JdbcDAO#create(java.lang.Object) 
    */ 
    @Override 
    public boolean create(UserEntity user) throws DAOException { 
     // TODO Auto-generated method stub 
     PreparedStatement ps = null; 
     ResultSet generatedKeyResultSet = null; 
     boolean created = false; 

     String SQL_CREATE = "INSERT INTO USER(FIRST_NAME, MIDDLE_NAME, LAST_NAME, EMAIL_ADDRESS"; 
     String sqlValues = "(?, ?, ?, ?"; 

     if (user.getGender() != null) { 
      SQL_CREATE += ", GENDER"; 
      sqlValues += ", ?"; 
     } 

     if (user.getBirthDate() != null) { 
      SQL_CREATE += ", DOB"; 
      sqlValues += ", ?"; 
     } 

     SQL_CREATE += ") VALUES " + sqlValues + ")"; 

     try { 
      ps = getConnection().prepareStatement(SQL_CREATE, Statement.RETURN_GENERATED_KEYS); 
      ps.setString(1, user.getFirstName()); 
      ps.setString(2, user.getMiddleName()); 
      ps.setString(3, user.getLastName()); 

      int pos = 4; 
      if (user.getGender() != null) { 
       ps.setString(pos++, user.getGender().toString()); 
      } 

      ps.setString(pos++, user.getEmailAddress()); 

      if (user.getBirthDate() != null) 
       ps.setDate(pos++, new Date(user.getBirthDate().getTime())); 

      ps.executeUpdate(); 
      generatedKeyResultSet = ps.getGeneratedKeys(); 
      if (generatedKeyResultSet != null && generatedKeyResultSet.next()) { 
       user.setId(generatedKeyResultSet.getLong(1)); 
      } 
      created = true; 
     } catch (SQLException e) { 
      // TODO Auto-generated catch block 
      throw new DAOException(e); 
     } finally { 
      try { 
       close(generatedKeyResultSet, ps); 
      } catch (SQLException e) { 
       // TODO Auto-generated catch block 
       logger.error("Error closing statement or resultset.", e); 
      } 
     } 

     return created; 
    } 

あなたのアプローチが悪いわけではありません。私たちはちょうどstatic finalブロックにDAOのSQLを含むことに慣れてきました。

SQLの行数が増える場合は、StringBuilder(同期なし)またはStringBuffer(同期あり)を文字列操作に使用できます。

0

私は、それらを私の瓶に詰め込まれた特別なプロパティファイルに入れていました。それから私はProperties.load(getClass().getResourceAsStream("queries.properties"))を使ってそれを抽出し、prepared statementを使用しました。

私はこのテクニックを前回使用して以来、何年も経ちましたが、今これを行うには深刻な理由がない限り、お勧めできません。

JPAの使い方は、大きなプロジェクトにとって「正しい」解決策だと思います。あなたが注釈としてクエリを書くことを可能にするiBatisのような、より小さなプロジェクト使用マッピングツールを開発しているなら。

0

静的クエリ(バインディングパラメータのみに依存するもの)は*DAOクラスに完全に収まります。これは、DBアクセスを抽象化するクラスで、loadUser(int userId)またはsaveUser(User user)などのDAO APIのみを処理します。このようにDAOにクエリを格納する方法は大きな問題ではありません。
私は通常動的クエリを使用しないので、私はそれらについて良い助言を与えることはできません。

2

私は個人的にこれらのクエリをXMLファイルに配置する傾向があります。プロパティファイルは、複雑なクエリにとって厄介なものです(クエリの各行の後に\を忘れないでください)。また、あなたがそれに取り組んでいる間に、シンプルなプロジェクトと複雑なプロジェクトの両方で使用することが喜ばしいiBatis (now MyBatis)のような単純なDAOフレームワークを使用するだけではいかがですか? :-)

0

あなたが調べたいことは、ストアドプロシージャまたはビューです。私はどのタイプのデータベースを使用しているのかよく分かっていませんが、MS SQLとMySQLではどちらもオプションです。長いクエリを格納するだけでなく、クエリを実行するのではなく変数を渡すので、恐怖のdun dun dunnnnnnn SQLインジェクションも保護されます。今では、アプリケーションの複雑さもわかりませんが、一般的には、クエリがデータベースのどこかのアプリケーションではなく、データベースのエンドに格納されるソリューションを使用する傾向があります。

読書のビット:(。ウィキの記事はい、しかし、下部の良い参照がある) http://en.wikipedia.org/wiki/Stored_procedure http://en.wikipedia.org/wiki/View_(database)

関連する問題