2016-08-12 2 views
0

Я знаю jdbcTemplate может быть использован для создания PreparedStatements, если вы установите его, чтобы сделать это:JbdcTemplate - PreparedStatements с Dynamic SQL Query

т.е.

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")); 
}); 

Однако, у меня есть запрос со многими и операторами в предложении Where, и в зависимости от некоторого условия, конкретная инструкция AND может быть добавлена ​​или не добавлена ​​в строку запроса.

т.е.

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

С этим типом динамического запроса, я не могу точно знать, сколько значений поместить в new Object[] {} поле во время компиляции, потому что someCondition поля всегда изменяются во время выполнения.

Есть ли способ написать this.jdbcTemplate.query(sqlQuery.toString(), new Object[] {}... для размещения этих динамических операторов И?

+0

Наверняка вы можете создать 'Object []' во время выполнения с соответствующим размером. что ты уже испробовал? – mustaccio

+0

@mustaccio Спасибо за этот совет! Это действительно помогло. Поскольку вы не можете изменять размеры массивов в Java во время выполнения, я смог придумать довольно простое решение для динамической инициализации 'Object []' с соответствующим размером во время выполнения. У меня все еще есть другая проблема, которую мне нужно исправить, прежде чем я опубликую свое решение, хотя – bscott

+0

Проблема в том, что один из динамических операторов 'AND' написан так:' AND column1 IN ('x', 'y', 'z', 'и т. д.') ', где значения внутри круглых скобок также будут динамическими во время выполнения. Я не могу написать подготовленноеStatement как 'AND column1 IN (?)', А затем подключить строковое значение, разделенное запятыми (то есть 'String values ​​=" x, y, z, etc ... ";'), потому что это будет обрабатывать строковое значение, разделенное запятыми, как строковый литерал, что приводит к ошибкам. Я в процессе исправления этой проблемы, и я вернусь, когда я ее исправил. – bscott

ответ

0

Я нашел решение после учета того, что @mustaccio сказал в своем комментарии к моему первоначальному вопросу. Я также принял участие в решении от this stackoverflow question и использовал его в своем собственном решении.

Основная проблема, с которой я столкнулся, заключалась в динамическом создании массива Object[] во время выполнения, поскольку вы не можете динамически добавлять элементы в массив Object[]. При инициализации они должны иметь определенный размер.

Во-первых, я создаю arraylist строк под названием queryArgs. Каждый раз, когда одно из условий if доказывается верно, и мы добавляем в запрос оператор AND, я также добавляю еще одну строку кода, которая добавляет значение, которое должно быть вставлено в подготовленноеStatement в arraylist queryArgs. Как только это будет сделано, я создаю новый массив Object[], размер которого инициализируется размером queryArgs arraylist. Наконец, я прокручиваю каждый элемент в массиве Object[], устанавливая их равными значениям в 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... */ 
    }); 
} 

останца является то, что один из динамических AND утверждений выше написано как это:

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

, где значение внутри скобок также является динамическим во время выполнения. Моя служба получает строковое значение, которое выглядит следующим образом:

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

Я не могу написать PreparedStatement так: AND column4 IN (?), а затем просто подключить в queryParams.value4, потому что он будет относиться к queryParams.value4 как строковый литерал, что приводит к ошибкам.

Чтобы решить эту проблему, я создаю еще один arraylist строк, называемый value4Array. Я прокручиваю каждый символ в queryParams.value4, и я проверяю, равен ли текущий символ в цикле запятой, нашим разделителем. Если это так, я создаю подстроку всех символов, предшествующих этой запятой, и добавьте эту вновь созданную строку в value4Array.

Следующий шаг - создать динамический оператор AND column4 IN (?).Я делаю это, перебирая каждое строковое значение в массиве value4Array, который мы только что создали, и делаем sql.append("?"), исходя из того, сколько строк находится в value4Array. После этого остальная часть логики совпадает с моим решением выше.

/* 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

... Я не удивлюсь, если для этого есть библиотека, которая может облегчить вам жизнь, но не знаю, что из себя делало. 'ArrayList' _does_ имеют метод' 'toArray()') (http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#toArray()), который должен упростить это часть кода. Для работы с параметром 'IN' будут полезны методы' String' 'split' и' join', а также 'ArrayList'' addAll'. –

+0

@ Clockwork-Muse Я не знал, как использовать метод 'ArrayList'' addAll'' или '' String'' в этой ситуации. Я все же удалил логику 'Object []' все вместе, используя 'toArray()', как вы предложили в 'List queryArgs' arrayList. Я также использовал метод 'split' вместо того, чтобы писать ненужную функцию parseValue4 для создания массива значений, поскольку нет смысла изобретать колесо. – bscott