在 Java 中准备动态语句的正确方法

The proper way to prepare a dynamic Statement in Java

提问人:locrizak 提问时间:8/27/2012 最后编辑:Dave L.locrizak 更新时间:8/27/2012 访问量:4411

问:

我正在根据通过查询字符串传递的值准备 SQL 语句。(我正在使用 Play! 框架。基本上,我遇到的(不是真正的问题,只是我不太喜欢的东西)是,当我想在SQL字符串中使用并在以后使用动态值设置它们时。?

这是我所拥有的:

String sql = "SELECT * FROM foobar_table WHERE";

if ( foo != 0 )
    sql += " AND foo=?";
if ( !bar )
    sql += " AND bar=?";

try{
    PreparedStatement getStmt = con.prepareStatement(sql);
    if ( foo != 0 )
        getStmt.setInt(1,foo);
    if ( foo != 0 && !bar )
        getStmt.setBoolean(2, bar);
    else
        getStmt.setBoolean(1, bar);
} catch (SQLException e ){
    e.printStackTrace();
}

这确实有效,但正如你所看到的,不是很直观。当有 2 个动态值时没关系,但当你达到 5 或 6 时,这只会变得荒谬。

有没有一种更简单的方法可以使其更加灵活,以便我知道如何以更好的方式填写所有内容??

java sql jdbc 准备语句 playframework-2.0

评论


答:

-1赞 mostar 8/27/2012 #1

这个怎么样:

String sql = "SELECT * FROM foobar_table WHERE 1=1";

if ( foo != 0 )
    sql += " AND foo=" + foo;
if ( !bar )
    sql += " AND bar=" + bar;

try{
    PreparedStatement getStmt = con.prepareStatement(sql);
} catch (SQLException e ){
    e.printStackTrace();
}

评论

3赞 locrizak 8/27/2012
这难道不会让一切都可以注射吗?准备声明的全部意义在于避免这种情况。
0赞 mostar 8/27/2012
您可以在输入此代码块之前检查参数。你没有提到应该假设变量是不安全的。
0赞 Glenn 8/27/2012
以此为基础,参数化,但也要建立一个对象的 ArrayList 以供以后设置:sql += " AND foo = ?"; paramList.add(foo);
0赞 locrizak 8/27/2012
这就是我在 php 中的做法,但由于 Java 是严格类型的,这将不起作用。 可以是 String、boolean、int、Float 或 doublefoo
0赞 Glenn 8/27/2012
for (Object param : paramList) { preparedStatment.setObject(param); }
3赞 Glenn 8/27/2012 #2

举个例子(太长了,无法评论):

String sql = "SELECT * FROM foobar_table WHERE 1 = 1 ";
ArrayList paramList = new ArrayList();

if ( foo != 0 ) {
    sql += " AND foo=?";
    paramList.add(foo);
}

if ( !bar ) {
    sql += " AND bar=?";
    paramList.add(bar);
}

try{

    PreparedStatement getStmt = con.prepareStatement(sql);

    int index = 1;

    for (Object param : paramList) {
        getStmt.setObject(index, param);
        index++;
    }

    // execute

} catch (SQLException e ){
    e.printStackTrace();
}

评论

0赞 locrizak 8/27/2012
像这样最好..很好,很干净。谢谢
0赞 Dave L. 8/27/2012 #3

也许是这样的东西(只是粗略的代码,但你明白了):

    class Param{
        final int type;
        final Object value;         
        Param(int type, Object value){
            this.type = type;
            this.value = value;
        }
    }

    final List<Param> params = new ArrayList<>();

    final StringBuilder sql = new StringBuilder("SELECT * FROM bcu_venue_events WHERE 1=1");
    int foo = 0;
    boolean bar = true;             
    if (foo != 0){
        sql.append(" AND foo=?");
        params.add(new Param(Types.INTEGER, foo));
    }       
    if (!bar){
        sql.append(" AND bar=?");
        params.add(new Param(Types.BOOLEAN, bar));
    }
    try(Connection conn = null; PreparedStatement getStmt = conn.prepareStatement(sql.toString())) {            
        for(int i = 0; i < params.size(); i++){
            Param p = params.get(i);
            getStmt.setObject(i + 1, p.value, p.type);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }