1

私は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)などのコンマ区切りの値を持つ文字列です。

私がすでにパラメータを持っているところ:String temp = "I、J、K"。

4

3 に答える 3

5

要素ごとに1つの疑問符が必要です。それを回避する方法はありません(Spring JDBCのようなラッパー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");
于 2013-02-11T10:01:03.287 に答える
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など)を使用したくない場合はうまくいきます。

于 2013-03-13T22:34:21.597 に答える
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で、は可変数の値を含む文字列配列です。

次に、値を割り当てることができます。

  if (names.length > 0) {
    for (int i = 0; i < names.length; i++) {
      stmt.setString (i + 1, names[i]);
    }
  }
于 2013-02-11T13:34:52.743 に答える