web-dev-qa-db-ja.com

読みやすく、保守可能で高速なSQLクエリ用の複数行の文字列を作成する方法は?

私はいくつかのSQLコマンドを持っていますが、それらをコードで使用するための最良の方法を理解しようとしています。
1。彼らは非常に読みやすいです
2。更新は簡単です
3。多くの文字列構成のため、パフォーマンスのオーバーヘッドにはなりません。

次のようなもので、あまりうまくいきませんでした。

    public class SQLHandler {  
      private static final String CUSTOMER_TABLE = "customer_table";  
      private static final String CUSTOMER_ID = "id";  
      private static final String CUSTOMER_FIRST_NAME = "first_name";    
      private static final String CUSTOMER_LAST_NAME = "last_name"; 
      private static final String CUSTOMER_TELEPHONE = "customer_telephone";  
      private static final String REP_ID = "customer_representative_id";    


      private static final String REP_TABLE = "representative_table";  
      private static final String REP_ID = "id";  
      private static final String REP_FIRST_NAME = "first_name";    
      private static final String LAST_LAST_NAME = "last_name"; 
      private static final String DEPT_ID = "rep_dep_id";  

      public static ArrayList<Representatives> getRepresentatives(int customerId) {  
       StringBuilder sb = new StringBuilder();
       sb.append("SELECT")   
       .append(REP_TABLE).append(".").append(REP_ID)  
       .append(",")   
       .append(REP_TABLE)  
       .append(".")   
       .append(REP_FIRST_NAME).append(" FROM")
       .append(CUSTOMER_TABLE).append(" JOIN ").append(REP_TABLE)   
       .append("ON").append(REP_TABLE).append(".")     
       .append(REP_ID).append("=").append(CUSTOMER_TABLE)    
      .append(".").append(REP_ID)  
      .append(" AND")  
      .append(CUSTOMER_TABLE).append(".").append(CUSTOMER_ID)   
      .append("=").append(String.valueOf(customerId));  

    // do query  
    }   
} 

ご覧のとおり、(3)のどれも満たされていません。
クエリを簡単に更新することはできず、もう一度見た場合、それが何であったか正確に思い出せません。
どうすればこれを改善できますか? (ポストで正しくフォーマットできませんでした)

4
Jim

私の提案は:

public class SQLHandler {  

    private String sql=
    "SELECT \n"+
    "   c.customer_representative_id, \n"+
    "   c.first_name \n"+
    "FROM \n"+
    "   customer_table c JOIN representative_table r ON \n"+
    "   r.customer_representative_id=c.id \n"+
    "WHERE \n"+
    "   customer_table.id=? \n";

    // methods and so on...

}

"\n"すべての行の末尾の文字は、デバッグ用のニース出力になります。

後でユーザーPreparedStatementを「?」に置き換えますしたがって、SQLインジェクションを防止します。

PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(customed_id);
ResultSet rs = ps.executeQuery();
/// etc...

別の代替方法は、ファイルからSQL文字列を読み取ることPropertiesクラスで実行できます。このようなファイルには複数のプロパティを含めることができ、各プロパティは異なるSQLクエリまたは必要なその他の種類の構成値です。

サンプルファイル(" \"はプロパティに複数行の値を許可しますが、1行として読み取られます)

SQL1=\
SELECT \
    c.customer_representative_id, \
    c.first_name \
FROM \
    customer_table c JOIN representative_table r ON \
    r.customer_representative_id=c.id \
WHERE \
    customer_table.id=? \

これは次のようにロードできます:

import Java.util.Properties;
///...
Properties prop = new Properties();
prop.load(new FileInputStream("sql_queries.conf"));
String sql = prop.getProperty("SQL1");

次に、PreparedStatementを使用します...

ボーナス提案:

頻繁に行う結合を使用してビューを作成します。これにより、次のように、コードまたはプロパティファイルでクエリがよりシンプルに見えます。

SELECT
    v.customer_representative_id,
    v.first_name,
        //other columns
FROM
    my_reps_view v
WHERE
    v.customer_table.id=?
5

生の文字列リテラル

生の文字列 リテラルをJava言語に導入する作業が進行中です。それに伴い、 複数行の文字列が来ます この質問で必要です。

JEP 326

JEP 326:Raw String Literals を参照してください。

抜粋:

生の文字列リテラルをJavaプログラミング言語に追加します。生の文字列リテラルは複数行のソースコードにまたがることができ、\ nのようなエスケープシーケンスや、\形式のUnicodeエスケープを解釈しません。 uXXXX…

java以外の文法を対象とする文字列を提供する…

新しい行に特別なインジケーターを提供することなく、ソースの複数の行にまたがる文字列を提供します。

SQLのネストは、JEPで提供されている使用例の1つです。

Brian Goetzによるステータスの更新

生の文字列リテラル-どこにいるのか、どうやってここに来たのか by Brian Goetz、2018-03-27を参照してください。

それらは、任意の数の backticks をraw-string-literal開始-停止 区切り文字 として使用する傾向があります。 1つのバックティックが最も一般的ですが、テキスト値にバックティックが含まれている場合は、それらを使用します。したがって、 エスケープ はまったく必要ありません。

抜粋:

…物事は生の文字列リテラルでほぼ安定しています…

    String ss = `a multi-
     line-string`;

ちなみに、 Java Strings With JDK 11]の新しいメソッド の記事は、Dustin Marxがこれは、Java 11.に追加される、Unicodeに対応した新しい文字列トリミングメソッドをいくつかカバーしています。これらは、最終的な生の文字列リテラル機能に関連している可能性があります。

3
Basil Bourque

完全を期すために、コンパイルに追加のステップを追加して、 multiline-string project を使用するなどして、javadocコメントから値を挿入し、次のようなものにすることができます。

/**
 * SELECT 'Hello world'
 * FROM dual
 */
@Multiline
public static String getHelloWorld;

コンパイル時にアノテーションを次のように置き換えます

public static String getHelloWorld= "SELECT 'Hello world'\nFROM dual";

そして、あなたがいくつかの懸念がある場合に備えて、私はそれをJPAエンティティマネージャーで使用しました

javax.persistence.Query qry = em.createQuery(QueryRepository.getHelloWorld); 

ここで、QueryRepositoryは従来の 定数のクラス です。

開発と使用に関するチュートリアル: http://www.adrianwalker.org/2011/12/Java-multiline-string.html

1
Ruslan López

「流暢」スタイルのオーバーヘッドが必要ない場合は、Javaクエリビルダー(jOOQなど))、次のような形式のクエリに静的文字列を使用しました。

public class SQLHandler {
    private static final String REPRESENTATIVES_QUERY =
        " SELECT" +
        "   representative_table.id," +
        "   representative_table.first_name," +
        " FROM" +
        "   customer_table" +
        " JOIN representative_table ON" +
        "   representative_table.id = customer_table.id" +
        " WHERE" +
        "   customer_table.id = ?";

    public static ArrayList<Representatives> getRepresentatives(int customerId) {
        // do query
    }
}
0
not22

参照: Multiple-line-syntax

また、複数行文字列の変数もサポートしています。次に例を示します。

String name="zzg";
String lines = ""/**~!{
    SELECT * 
        FROM user
    WHERE name="$name"
}*/;
System.out.println(lines);

出力:

SELECT * 
    FROM user
    WHERE name="zzg"
0
Sanjiv

大規模で複雑なクエリの場合、通常は文字列テンプレートを使用します。このように、クエリを変更したい場合、それは大きな.sql起動する構文の強調表示で編集できるファイル。さまざまな文字列テンプレートライブラリがたくさんあるので、一般化しておきます。

テンプレートを処理することは、一連の文字列を連結することよりも明らかに遅くなりますが、メンテナンスを容易にするために私にとってそれは価値がある場合があります。

文字列テンプレート

SELECT *
FROM table1
JOIN table2 ON table1.this = table2.that
WHERE table1.foo = ?
AND table2.baz = ?

Java

StringTemplate template = new MyQueryTemplate().
String query = template.process();
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, "bar");
statement.setString(2, "boo");
ResultSet resultSet = statement.executeQuery();
0
Matt