SQL中的动态绑定

时间:2013-02-11 09:54:09

标签: java sql jdbc

我正在尝试在我的SQL查询中动态绑定变量,我试图通过java执行。

为此,我使用数据绑定成功构建了以下查询:

select HOST,PORT, VIRTUAL_HOST, CLUSTER from XYZ where TYPE='abc' and NAME=?

但对于像以下的查询:

select HOST, PORT, VIRTUAL_HOST, CLUSTER from PQR where TYPE='abc' and NAME IN (?)

如何为IN语句使用动态绑定? 这里我绑定变量的值是一个带有逗号分隔值的字符串,例如NAME IN(I,J,K)

我已经有一个参数: 字符串temp =“I,J,K”。

3 个答案:

答案 0 :(得分:5)

每个元素需要一个问号,ad无法绕过它(除非使用像Spring一样的包装API为你做这个):

String sql = "select HOST, PORT, VIRTUAL_HOST, CLUSTER"
             + " from PQR where TYPE='abc' and NAME IN (?, ?, ?)";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, "I");
stmt.setString(2, "J");
stmt.setString(3, "K");

答案 1 :(得分:2)

您也可以编写自己的解决方法来完成与JDBC中的NamedParameterStatement相同的操作:

public class NamedParameterStatement {

    // define the parameter format as a colon followed by at least one word letter
    private final String PARAMETER_REGEX =":\\w+";

    // the parsed sql query string containing question marks,
    // not named parameters
    private String sql;

    // hashtables for parameter mapping
    private Hashtable<String, Integer> parameterNameMap;
    private Hashtable<Integer, Object> parameterValueMap;



    /**
     * constructor for named parameter statements
     * @param namedParameterSQL the sql query with named parameters
     */
    public NamedParameterStatement(String namedParameterSQL) {
        // init hashtables
        this.parameterNameMap = new Hashtable<String, Integer>();
        this.parameterValueMap = new Hashtable<Integer, Object>();

        // create a matcher for the named parameter sql query based on 
        // the pattern specified for the named parameter format
        Matcher m = Pattern.compile(
                PARAMETER_REGEX,
                Pattern.CASE_INSENSITIVE)
                .matcher(namedParameterSQL);
        int i = 1;
        while(m.find()) {
            // watch out: this implementation does not check
            // for multiple parameters with the same name.
            parameterNameMap.put(m.group().replace(":", ""), i++);
        }
        // replace all named parameters with question marks
        this.sql = m.replaceAll("?");
    }



    /**
     * creates a prepared statement for the specified connection,
     * based on the parameter mapping
     * @param conn the database connection
     * @return a prepared statement
     * @throws SQLException thrown on error with preparing the statement
     * @throws InvalidBindingException thrown on errors with the parameter binding
     */
    public PreparedStatement createPreparedStatement(Connection conn)
            throws SQLException, InvalidBindingException {
        // check if bindings match
        if(parameterNameMap.size() == parameterValueMap.size()) {
            // create prepared statement
            PreparedStatement ps = conn.prepareStatement(this.sql);
            // for each parameter binding, set the parameter and its index accordingly
            for(Integer i : this.parameterValueMap.keySet()) {
                Object value = this.parameterValueMap.get(i);
                // map the parameter object types against the different parameter setters
                // this mapping is incomplete!!! just a proof of concept.
                if(value.getClass() == String.class) {
                    ps.setString(i, (String) value);
                } else if(value.getClass() == Integer.class || value.getClass() == int.class) {
                    ps.setInt(i, (int) value);
                } else if(value.getClass() == Double.class || value.getClass() == double.class) {
                    ps.setDouble(i, (double) value);
                } else if(value.getClass() == Long.class || value.getClass() == long.class) {
                    ps.setLong(i, (long) value);
                }
            }
            return ps;
        }
        else throw new InvalidBindingException("Not all parameters were bound.");
    }



    /**
     * returns the converted prepared statement query string
     * @return the query string
     */
    public String getSQL() {
        return this.sql;
    }



    /**
     * binds a parameter value to a parameter name
     * @param parameter the parameter name
     * @param value the parameter value
     */
    public void bindParameter(String parameter, Object value) {
        // check if the parameter name existed in the named parameter query string
        if(this.parameterNameMap.containsKey(parameter)) {
            this.parameterValueMap.put((Integer)this.parameterNameMap.get(parameter), value);
        }
        else throw new IllegalArgumentException("Parameter '" + parameter + "' does not exist.");
    }

}

用法:

NamedParameterStatement nps = new NamedParameterStatement(
    "SELECT * FROM table WHERE column1=:param1 AND column2=:param2");
nps.bindParameter("param1", "value1");
nps.bindParameter("param2", 2);
PreparedStatement ps = nps.createPreparedStatement(conn);
ResultSet results = ps.executeQuery();
while(results.next()) {
    System.out.println(results.getString("column1"));
}

我知道,这也不理想,但如果您不想使用JDBC或其他框架(Hibernate,...),它就可以解决问题。

答案 2 :(得分:1)

实际上你可以用这种方式动态地做到这一点:

  StringBuilder scmd = new StringBuilder (128);
  scmd.append ("SELECT HOST, PORT, VIRTUAL_HOST, CLUSTER ");
  scmd.append ("FROM PQR ");
  scmd.append ("WHERE TYPE='abc' ");
  if (names.length > 0) {
    scmd.append ("AND NAME IN (");
    for (int i = 0; i < names.length; i++) {
      if (i > 0)
        scmd.append (',');
      scmd.append ('?');
    }
    scmd.append (")");
  }
  PreparedStatement stmt = connection.prepareStatement(scmd.toString());

其中names是一个包含可变数量值的String数组。

然后你可以分配值:

  if (names.length > 0) {
    for (int i = 0; i < names.length; i++) {
      stmt.setString (i + 1, names[i]);
    }
  }