为PreparedStatements动态设置参数

时间:2016-11-09 04:48:42

标签: java jdbc prepared-statement

我很好奇,我们可以动态输入参数吗?例如,我有一个像这样的对象:

class Fields {
  private Integer id;
  private String name;

  public Fields(Integer id, String name) {
    this.id = id;
    this.name = name;
  }
  //Getter - setter here
}

现在,我准备声明:

public void main() {
  PreparedStatement ps = conn.PrepareStatement("SELECT * FROM table WHERE id = ? AND name = ?");`
}

然后我设置

Fields param = new Fields(2, "xxx");

嗯..叫我懒,怎样才能把参数发送到PrepareStatement,如

ps.setParameters(param)

或者也许:

ps.setParameters(param.getAsArray())

为了更好地说明我的观点,我想做一些类似的事情:

for (Fields f: fieldList) {
  sql += f + " = ?";
}
请原谅我的遗失" AND" /" OR"上述代码的运算符。我的最终目标是通过仅填充部分字段来传递实体类,从而创建整个字段和相应参数。然后代码将跳过空字段并将填充的字段作为参数。

好吧,只是为了好奇。

谢谢

5 个答案:

答案 0 :(得分:1)

您可以直接将值放在参数中,而不使用 Field 类。

ps.setParameters(2, "xxx");

试试这些,希望它对您有用。

    Field field1 = new Field(0,1,"id");
    Field field2 = new Field(1,"Nathan","name");

    List<Field> fields = new ArrayList<>();
    fields.add(field1);
    fields.add(field2);

    StringBuilder strBuilder = new StringBuilder("SELECT * FROM table WHERE ");

    // Dynamically Construct your SQL Query String
    for(int i = 0 ; i < fields.size() ; i++) {
        String strTemp = " AND ";
        String strTempEquals = " = ?";
        if(fields.size()-1 == i) {
            strTemp = ";";
        }
        strBuilder.append(fields.get(i).getColumnName());
        strBuilder.append(strTempEquals);
        strBuilder.append(strTemp);
    }

        PreparedStatement ps = conn.PrepareStatement(strBuilder);

    // Dynamically pass your values from your Field class to the parameters of PreparedStatement
    for (Field tempField : fields) {
        ps.setParameter(tempField.getIndex(),tempField.getValue());
    }

我在字段类中进行了一些更改。

class Field {
private Integer index;
private Object value;
private String columnName;

// Constructor
//Getters and Setter
}

答案 1 :(得分:1)

尝试使用像这样的参数化构造函数我希望这是你需要的

class Fields {
private Integer id;
private String name;
 public Fields(int id,String name)
 {
    this.id=id;
    this.name=name;
    setParameter();
 }
 public  void setParameter()
 {
    PreparedStatement ps = conn.PrepareStatement("SELECT * FROM table WHERE id = ? AND name = ?");
    ps.setInt(1,id);
    ps.setString(2,name);
 }
}

答案 2 :(得分:1)

好的..这是一个非常粗糙的解决方案。我仍然不喜欢这样一个事实,即我必须重复所有字段两次以获得字段的字符串或其他类型,但随时可以进一步完善它,因为这仍然非常粗糙。此外,您需要在ps.setObject()failafe之前扩展下面的ps.setSomething()参数。您还需要在没有任何条件的情况下预测查询,例如跳过那些标准对象扫描并直接执行。但我希望你明白这一点。

public static String addPrefix(String prefix, String field) {
    return new StringBuilder(prefix)
            .append(Character.toUpperCase(field.charAt(0)))
            .append(field.substring(1))
            .toString();
}

public static <T> List<T> query(Connection conn, T criteria, String operator) throws SQLException {
    List<T> list = null;
    Class<?> targetClass = criteria.getClass();
    if (targetClass.getAnnotation(Table.class) == null) throw new SQLException("ERROR: Table not defined at entity class " + targetClass.getName());
    StringBuilder SQL = new StringBuilder("SELECT * FROM ").append(targetClass.getAnnotation(Table.class).name());
    List<Object> parameters = new ArrayList<>();

    try {
        Field[] fields = targetClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Column.class) == null) continue;
                Method m = targetClass.getMethod(addPrefix("get", field.getName()).toString());
                Object o = m.invoke(criteria);

                if (o == null) continue;

                if (parameters.isEmpty()) SQL.append(" WHERE"); else SQL.append(operator);
                SQL.append(" ").append(field.getAnnotation(Column.class).name()).append(" = ?");
                parameters.add(o);
        }

        try (Connection connection = IwiPrivate.getInstance().getConnection()) {
            try (PreparedStatement ps = connection.prepareStatement(SQL.toString())) {
                Integer x = 1;
                for (Field field : fields) {
                    String type = field.getType().getName();
                    Method m = targetClass.getMethod(addPrefix("get", field.getName()));
                    Object o = m.invoke(criteria);

                    if (o == null) continue;
                    if (type ==  "java.lang.String") ps.setString(x, (String) parameters.get(x));
                    else if (type == "java.lang.Integer") ps.setInt(x, (Integer) parameters.get(x));
                    else ps.setObject(x, parameters.get(x)); //Put more set traps here.
                }
                try (ResultSet rs = ps.executeQuery();) {
                    while (rs.next())
                        list.add((T) Database.mapSingle(rs, targetClass));
                }
            }
        }
    } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        Logger.getLogger(QueryExperiment.class.getName()).log(Level.SEVERE, null, ex);
    }
    return list;
}

现在,要使用它,只需像这样创建实体对象

@Table(name = "testTable")
public class Entity {
    @Column(name = "id")
    private Integer id;

    @Column(name = "name")
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后将其作为标准

public void testQuery() {
    Entity criteria = new Entity();
    criteria.setId(7777);
    try (Connection connection = yourDatabase.getConnection()) {
        List<Entity> assets = QueryTest.query(connection, criteria, "AND");
    } catch (SQLException ex) {
        Logger.getLogger(IwiPrivateTest.class.getName()).log(Level.SEVERE, null, ex);
    }
}

它将创建一个SQL: SELECT * FROM testTable WHERE id = ? 并使用setInt

将7777作为参数发送

如果你想避免使用ORM并创建简单的查询,我相信这种方法可以正常工作。

答案 3 :(得分:0)

PreparedStatement有一个setObject(int, java.lang.Object)方法。您可以在接受字段数组/列表的类或方法中使用它,并将名称附加到sql并使用setObject方法设置参数。

请记住,此方法极易受到SQL注入攻击,因此请谨慎使用。

答案 4 :(得分:0)

在Java代码中,使用spring jdbc也可以实现这一点。

以下示例代码可供参考:目前,我们正在循环更新100个参数。如果我们知道参数的数量,那么我们就这样做。 如果您的条件是具有100个参数的表,则可以按照以下方法进行一些修改。

  public Class MappingDTO{
     private String attribute1;
     private String attribute2;
     private String attribute3;
     private String attribute4;
     .
     .
     .
     .
     .
     private String attribute100;

     public getAttribute1(){
     return attribute1;
     }
     .
     .
     .
     public getAttribute100(){
     return attribute1;
     }

}

  updated=jdbcTemplate.batchUpdate("our query", new BatchPreparedStatementSetter() {

                        @Override
                        public void setValues(PreparedStatement ps, int index) throws SQLException {
                                        MappingDTO mappingDTO = mappingDTOList.get(index);
                                        Class<?> tClass = mappingDTO.getClass();
                                        int i=1;
                                        for (i = 1; i <= 100; i++) { //100 max attributes can be updated //dynamic preparestatemetn setter
                                            Method method=null;
                                            try {
                                                method = tClass.getMethod("getAttribute"+(i), new Class[] {});
                                            } catch (NoSuchMethodException e) {
                                                LOG.error("Exception thrown | uploadStatementTemporaryTableUpdate | No such method exception | while trying to find methods", e);
                                            } catch (SecurityException e) {
                                                LOG.error("Exception thrown | uploadStatementTemporaryTableUpdate |Security Exception | while trying to find methods", e);
                                            }
                                            String str1=null;
                                            try {
                                                str1 = (String)method.invoke(mappingDTO, new Object[] {});
                                            } catch (IllegalAccessException e) {

                                            } catch (IllegalArgumentException e) {
                                                // TODO Auto-generated catch block

                                            } catch (InvocationTargetException e) {

                                            }
                                            ps.setString(i, str1);

                                        }


                                    }

                                    @Override
                                    public int getBatchSize() {
                                        // TODO Auto-generated method stub
                                        return mappingDTOList.size();
                                    }
                                });