Come creare stringhe multilinea per query SQL che sono leggibili, manutenibili e veloci?

2

Ho alcuni comandi SQL che sto cercando di trovare il modo migliore per averli nel codice in modo che:
1. Sono molto leggibili
2. Sarebbero facili da aggiornare
3. Non saranno le spese generali dovute a molte stringhe.

Ho qualcosa come la seguente che non è risultata troppo buona.

    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  
    }   
} 

Come puoi vedere, nessuno dei (3) è soddisfatto.
Non riesco facilmente ad aggiornare la query e se l'avessi vista di nuovo non ricorderei esattamente di cosa si trattava.
Come posso migliorare questo? (Impossibile formattarlo correttamente in post)

    
posta Jim 29.07.2016 - 19:16
fonte

6 risposte

4

Il mio suggerimento è:

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...

}

I caratteri "\n" alla fine di ogni riga consentono una buona stampa a scopo di debug.

Utente successivo a PreparedStatement per sostituire "?" impedendo così l'iniezione sql:

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

Un'altra alternativa è la lettura della stringa SQL da un file che può essere eseguita con la classe Properties . Tale file può avere più proprietà, ognuna delle quali è una query SQL diversa o altri tipi di valori di configurazione che si desidera.

File di esempio (il " \" deve consentire il valore multilinea per una proprietà ma viene letto come una singola riga)

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=? \

Quale può essere caricato in questo modo:

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

Quindi utente a PreparedStatement ...

Suggerimento bonus:

Crea visualizzazioni con i join che farai frequentemente, in modo che le query risultino più semplici nel codice o in altri file di proprietà, come:

SELECT
    v.customer_representative_id,
    v.first_name,
        //other columns
FROM
    my_reps_view v
WHERE
    v.customer_table.id=?
    
risposta data 29.07.2016 - 20:59
fonte
1

Solo per completezza è possibile aggiungere un ulteriore passaggio alla compilazione per consentire di iniettare il valore dal commento di javadoc come usando progetto multiline-string e avere qualcosa di simile:

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

Sostituirà l'annotazione al momento della compilazione con

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

E nel caso avessi qualche dubbio, l'ho usato con un gestore di entità JPA come

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

dove QueryRepository è una classe per costanti convenzionale

convenzionale.

Tutorial su sviluppo e utilizzo: link

    
risposta data 05.07.2017 - 19:03
fonte
1

Letterali stringa raw

Il lavoro è in corso per introdurre stringa di testo nella lingua Java. Insieme a questo vengono fornite le stringhe multilinea desiderate in questa domanda.

JEP 326

Vedi JEP 326: Raw String Literals .

Estratti:

Add raw string literals to the Java programming language. A raw string literal can span multiple lines of source code and does not interpret escape sequences, such as \n, or Unicode escapes, of the form \uXXXX …

supply strings targeted for grammars other than Java, …

supply strings that span several lines of source without supplying special indicators for new lines.

Nesting SQL è uno dei casi d'uso di esempio forniti nel JEP.

Aggiornamento di stato di Brian Goetz

Vedi Valori stringali raw - dove siamo, come siamo arrivati qui di Brian Goetz, 2018-03-27.

Si stanno orientando verso l'utilizzo di un numero qualsiasi di backtick come interruzione iniziale della stringa raw delimitatori . Un singolo apice sarebbe più comune, ma usarne un numero se il valore del testo contiene degli apici inversi. Quindi non è necessaria nessuna ricerca di escape .

Estratti:

… things have largely stabilized with raw string literals …

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

A proposito, potresti trovare l'articolo Nuovi metodi su Java Strings With JDK 11 di Dustin Marx per essere interessante. Copre alcuni nuovi metodi di taglio delle stringhe basati su Unicode che vengono aggiunti a Java 11. Questi possono essere correlati alla caratteristica di stringhe di stringa non formattate.

    
risposta data 03.05.2018 - 08:46
fonte
0

Se non vuoi il sovraccarico di un generatore di query Java "fluente" (come jOOQ o qualcosa del genere), ho usato le stringhe statiche per le query formattate in questo modo:

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
    }
}
    
risposta data 29.07.2016 - 21:18
fonte
0

Per query complesse di grandi dimensioni, di solito utilizzo un modello di stringa. In questo modo, quando voglio cambiare la query, è solo un grosso file .sql che posso modificare con l'evidenziazione della sintassi per l'avvio. Esistono molte librerie di modelli di stringhe diverse che lascerò generalizzate.

È decisamente più lento elaborare il modello piuttosto che concatenare un gruppo di stringhe, ma a volte ne vale la pena per la facilità di manutenzione.

Modello di stringa

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();
    
risposta data 06.07.2017 - 05:09
fonte
0

Vedi: sintassi a più linee

Supporta anche le variabili nella stringa multilinea, ad esempio:

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

Output:

SELECT * 
    FROM user
    WHERE name="zzg"
    
risposta data 15.09.2017 - 17:29
fonte

Leggi altre domande sui tag