JbdcTemplate - PreparedStatements with Dynamic SQL Query



我知道jdbcTemplate可以用来创建PreparedStatements,如果你设置它这样做:

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操作符的查询,并且根据某些条件,特定的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[] {}...以适应这些动态AND语句?

在考虑了@mustaccio在他对我最初问题的评论后,我找到了一个解决方案。我还从这个stackoverflow问题中提取了一部分解决方案,并将其用于我自己的解决方案中。

我遇到的主要问题是在运行时动态创建Object[]数组,因为你不能动态地将元素添加到Object[]数组。它们在初始化时必须有一个定义的大小。

首先,我创建了一个名为queryArgs的字符串数组列表。每当其中一个if条件证明为真并且我们向查询添加AND语句时,我还添加另一行代码,将要插入preparedStatement中的值添加到queryArgs数组列表中。完成之后,我创建一个新的Object[]数组,其大小初始化为queryArgs数组列表的大小。最后,循环遍历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视为字符串字面量,这会导致错误。

为了解决这个问题,我创建了另一个字符串数组列表value4Array。循环遍历queryParams.value4中的每个字符,并检查循环中的当前字符是否等于逗号(分隔符)。如果是,那么我创建一个包含逗号前面所有字符的子字符串,并将新创建的字符串添加到value4Array

下一步是创建动态AND column4 IN (?)语句。我通过循环遍历我们刚刚创建的value4Array数组列表中的每个字符串值,并根据value4Array中有多少个字符串执行sql.append("?")来实现这一点。在此之后,其余的逻辑与上面的解决方案相同。

/* 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));
    }
}

您正在获取字符串类型(List<String> queryArgs = new ArrayList<>();)的列表。如果参数是整数类型会怎样?是否有任何方法来存储多种类型的数据?

最新更新