2016-08-12 1 views
0

Ich weiß jdbcTemplate verwendet werden kann PreparedStatements zu erstellen, wenn Sie es einrichten, dies zu tun up:JbdcTemplate - PreparedStatements mit dynamischem SQL Query

dh

private JdbcTemplate jdbcTemplate; 
String sqlQuery = "Select * from table where column = ?"; 
String value = "value"; 
this.jbdcTemplate.query(sqlQuery, new Object[] { value }, rs, rowNum -> { 
    System.out.println(rs.getString("column")); 
}); 

Allerdings habe ich eine Abfrage mit vielen AND-Operatoren In der Where-Klausel und abhängig von einer Bedingung kann eine bestimmte AND-Anweisung zur Abfragezeichenfolge hinzugefügt werden oder nicht.

heißt

private JdbcTemplate jdbcTemplate; 
StringBuilder sqlQuery = new StringBuilder("Select * from table where 1=1 "); //base query 
if(someCondition) 
    sqlQuery.append("And column1 = '" + value1 + "'"); 
if(someCondition2) 
    sqlQuery.append("And column2 = '" + value2 + "'"); 
if(someCondition3) 
    sqlQuery.append("And column3 = '" + value3 + "'"); 
//etc... 

Bei dieser Art der dynamischen Abfrage, ich bin nicht in der Lage, genau zu wissen, wie viele Werte bei der Kompilierung im new Object[] {} Feld zu platzieren, weil die someCondition Felder werden immer zur Laufzeit zu ändern.

Gibt es eine Möglichkeit, die this.jdbcTemplate.query(sqlQuery.toString(), new Object[] {}... zu schreiben, um diese dynamischen UND-Anweisungen unterzubringen?

+0

Sicher können Sie das 'Objekt []' zur Laufzeit mit der entsprechenden Größe erstellen. Was hast du bisher versucht? – mustaccio

+0

@mustaccio Danke für diesen Hinweis! Das hat wirklich geholfen. Da Sie zur Laufzeit die Größe von Arrays in Java nicht ändern können, war es mir möglich, eine ziemlich einfache Lösung zu finden, um das 'Objekt []' zur Laufzeit dynamisch mit der passenden Größe zu initialisieren. Ich habe noch ein anderes Problem, das ich beheben muss, bevor ich meine Lösung poste – bscott

+0

Das Problem ist, dass eine der dynamischen AND-Anweisungen wie folgt geschrieben wird: 'AND column1 IN ('x', 'y', 'z', 'etc ..') ', wobei die Werte innerhalb der Klammern zur Laufzeit ebenfalls dynamisch sind. Ich kann nicht die preparedStatement wie 'AND column1 IN (?)' Schreiben und dann den kommagetrennten String-Wert (dh 'String Werte =" x, y, z, etc ... ";") in, weil es behandelt den kommagetrennten Zeichenfolgenwert als Zeichenfolgenliteral, was zu Fehlern führt. Ich bin dabei, dieses Problem zu beheben, und ich werde zurückkommen, wenn ich es behoben habe. – bscott

Antwort

0

Ich fand eine Lösung nach Berücksichtigung, was @mustaccio in seinem Kommentar zu meiner ursprünglichen Frage sagte. Ich nahm auch einen Teil der Lösung von this stackoverflow question und benutzte es in meiner eigenen Lösung.

Das Hauptproblem ich dynamisch eine Object[] Array zur Laufzeit zu schaffen wurde mit, da man nicht dynamisch Elemente zu einem Object[] Array hinzufügen kann. Sie müssen eine definierte Größe haben, wenn sie initialisiert werden.

Zuerst erstelle ich eine Arraylist der Zeichenketten queryArgs. Jedes Mal, wenn eine der if-Bedingungen wahr ist und wir der Abfrage eine AND-Anweisung hinzufügen, füge ich auch eine weitere Codezeile hinzu, die den Wert hinzufügt, der in preparedStatement in die queryArgs-Arraylist eingefügt werden soll. Sobald das erledigt ist, erstelle ich ein neues Object[] Array, dessen Größe auf die Größe der queryArgs Arraylist initialisiert wird. Schließlich durchlaufe ich jedes Element im Array Object[] und setze sie gleich den Werten in queryArgs.

private JdbcTemplate jdbcTemplate; 

List<QueryResults> jdbcQuery(QueryParams queryParams) { 
    /* base query */ 
    StringBuilder sqlQuery = new StringBuilder("Select * from table where 1=1 "); 
    /* stores the dynamic preparedStatement arguments */ 
    List<String> queryArgs = new ArrayList<>(); 

    if(someCondition){ 
     sqlQuery.append("And column1 = ? "); 
     queryArgs.add(queryParams.value1); 
    } 
    if(someCondition2){ 
     sqlQuery.append("And column2 = ? "); 
     queryArgs.add(queryParams.value2); 
    } 
    if(someCondition3){ 
     sqlQuery.append("And column3 = ? "); 
     queryArgs.add(queryParams.value3); 
    } 
    //etc... 

    /* this is the part I used from the above stackoverflow question */ 
    Object[] preparedStatementArgs = new Object[queryArgs.size()]; 
    for(int i = 0; i < preparedStatementArgs.length; i++){ 
     preparedStatementArgs[i] = queryArgs.get(i); 
    } 

    /* Lastly, execute the query */ 
    return this.jdbcTemplate.query(sqlQuery.toString(), 
    preparedStatementArgs, (rs, rowNum) -> { 

     QueryResults result = new QueryResults(); 
     /* store the results of the query... */ 
    }); 
} 

Der Ausreißer ist, dass eines der dynamischen AND Aussagen oben wie folgt geschrieben wird:

AND column4 IN ('x','y','z','etc..'‌​) 

, wobei die Werte innerhalb der Klammern zur Laufzeit auch dynamisch sind. Mein Service erhält einen String-Wert, der wie folgt aussieht:

String queryParams.value4 = "x,y,z,etc..."; 

ich die preparedStatement wie dies nicht schreiben kann: AND column4 IN (?) und dann stecken Sie einfach in queryParams.value4 weil es queryParams.value4 als Stringliteral behandeln, was zu Fehlern führt.

Um dieses Problem zu lösen, erstelle ich eine andere Arraylist der Zeichenfolgen value4Array. Ich durchlaufe jedes Zeichen in queryParams.value4, und ich überprüfe, ob das aktuelle Zeichen in der Schleife einem Komma entspricht, unserem Begrenzer. Wenn dies der Fall ist, erzeuge ich eine Teilzeichenkette aller Zeichen, die zu diesem Komma führen, und füge diese neu erzeugte Zeichenkette zu value4Array hinzu. Der nächste Schritt besteht darin, die dynamische AND column4 IN (?)-Anweisung zu erstellen.Ich mache das, indem ich jeden String-Wert in der value4Array Arraylist, die wir gerade erstellt haben, durchläuft und eine sql.append("?") macht, basierend darauf, wie viele Strings in value4Array sind. Danach ist der Rest der Logik derselbe wie meine obige Lösung.

/* this function takes the comma delimited string literal (value4 : "x,y,z,etc...") 
    and parses it into an array of strings. */ 
private List<String> parseValue4(String value4){ 
    int valueIndex= 0; 
    List<String> value4Array = new ArrayList<>(); 
    for(int i = 0; i < value4.length(); i++){ 
     if(value4.charAt(i) == ','){ 
      value4Array.add(value4.substring(valueIndex, i)); 
      valueIndex = i + 1; 
     } 
     else if(i == value4.length() - 1){ 
      value4Array.add(value4.substring(valueIndex, value4.length())); 
     } 
    } 
    return value4Array; 
} 
if(someCondition4){ 
    List<String> value4Array = parseValue4(queryParams.value4); 
    sqlQuery.append("And column4 IN ("); /* base AND statement */ 
    for(int i = 0; i < value4Array.size(); i++){ 
     if(i == value4Array.size() - 1) 
      sqlQuery.append("?)"); 
     else      /* dynamically appending ?'s */ 
      sqlQuery.append("?,"); 
     queryArgs.add(value4Array.get(i)); 
    } 
} 
+0

... Ich wäre nicht überrascht, wenn es dafür eine Bibliothek gäbe, die Ihnen die Sache erleichtern könnte, aber nichts davon versteht. 'ArrayList' _does_ hat eine [' toArray() '] (http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#toArray()) Methode, die das vereinfachen sollte Teil des Codes. Für den Umgang mit dem 'IN'-Parameter sind die' String'-Methoden 'split' und' join' hilfreich, ebenso wie 'ArrayList's' addAll'. –

+0

@ Clockwork-Muse Ich war mir nicht sicher, wie man in dieser Situation die 'ArrayList'' addAll' Methode oder die 'String'' Join' Methode benutzt. Ich habe jedoch die 'Object []' Logik alle zusammen mit 'toArray()', wie Sie in der 'List queryArgs' ArrayList vorgeschlagen. Ich habe auch die 'split' Methode benutzt, anstatt diese unnötige' parseValue4' Funktion zu schreiben, um das Array von Werten zu erstellen, da es keinen Sinn macht das Rad neu zu erfinden. – bscott