【问题标题】:Java SQL Prepared Statement Issue with Array and Where... IN List [duplicate]数组和 Where 的 Java SQL 准备语句问题... IN 列表 [重复]
【发布时间】:2019-03-30 12:47:22
【问题描述】:

我有一个包含字符串的数组列表:

ArrayList<String> anrs = new ArrayList<>();

现在我将它们转换成这样的 SQL 数组:

final String[] data = anrs.toArray(new String[anrs.size()]);
final java.sql.Array sqlArray = connection.createArrayOf("varchar", data);

现在我想做一个这样的准备好的声明:

statement = connection.createStatement();
String selectSQL = "SELECT * FROM rekopf INNER JOIN repos ON rekopf.rekopfnum=repos.Reposnum WHERE repos.reposart IN ?";
pstatement = connection.prepareStatement(selectSQL);
pstatement.setArray(1, sqlArray);
resultSet = pstatement.executeQuery();

但我得到了这个错误:

net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.1 意外 令牌:?必填:

此行出现错误:pstatement = connection.prepareStatement(selectSQL);

在这种情况下我的问题是什么?提前致谢。


当我这样尝试时更新:

pstatement.setArray(1, connection.createArrayOf("varchar", data));

然后我得到了这个错误

net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::4.0.1 转换中的数据类型不兼容

【问题讨论】:

  • 我已经做了一些谷歌搜索。似乎问题在于?在您准备好的语句中应该代表一个值。大多数建议都在讨论动态创建一个字符串,如 "(?,?,?,...?)" 为尽可能多的 ?随你喜欢,然后设置所有这些。但是,这确实消除了您从使用 PS 中获得的效率提升。尽管您确实从 SQL 注入保护中获益。希望这会有所帮助?
  • 嗯,好的,谢谢。我可以用 normale 语句做到这一点吗?
  • 别这么认为,你没有 setArray(n,array) 方法。此外,如果您只是手动创建没有 PS 的查询,您将自己设置为 SQL 注入?我会选择动态 (?,?,?,..?) 创建方法。哦是的
  • 哦,是的,显然 PS 可以拥有的参数数量是有限制的,具体取决于哪个 DB。例如1000 为甲骨文。

标签: java sql arraylist prepared-statement


【解决方案1】:

java.sql.Array 不等同于任何数据类型的 java 数组。一些 DB 具有对 Collections 和 Records 类型的内置支持(例如 Oracle 中的 Varray)。 java.sql.Array 可以方便地从客户端发送此类数据类型的数据(主要是过程 PL/SQL 的参数)。有关更多信息,您可能需要阅读有关您选择的数据库的集合、记录和类型支持。

根据您的需要,您只需要编写一个 SQL,其中 List 中的每个元素都成为 IN 子句的一部分

例如:select * from user where name in ('Hans', 'Joe', 'Jane');

    //Your below code snippet, 
    ArrayList<String> anrs = new ArrayList<>(); 
    // may be generated elsewhere as
    // List<String> anrs = Arrays.asList("Hans", "Joe", "Jane");
    // can be flattened to comma separated, quoted String
    String inClause = anrs.stream().map(s -> "'"+s+"'")
                      .collect(Collectors.joining(","));       
    System.out.println("List<String> flattened for IN Clause -> "+inClause);
    // which you will use to create SQL
    String selectSQL = String.format("SELECT * FROM rekopf INNER JOIN repos "
        + "ON rekopf.rekopfnum=repos.Reposnum WHERE repos.reposart IN (%s)", 
        inClause);
    java.sql.PreparedStatement pstatement = 
                            connection.prepareStatement(selectSQL);
    java.sql.ResultSet resultSet = pstatement.executeQuery();

    while(resultSet.next()){
        //TODO - process the result
    }

为带有 In 子句的 SQL 将参数绑定到 PreparedStatement 并不简单。我把这个留给你练习,以防你想超越。

【讨论】:

    【解决方案2】:

    问题可能在于使用? 作为占位符。

    试试...WHERE repos.reposart IN (?)

    如发现here

    【讨论】:

      【解决方案3】:

      是的,就像其他人说的那样?是问题。

      在这种情况下,我会这样做:

      String selectSQL = "SELECT * FROM rekopf INNER JOIN repos ON rekopf.rekopfnum=repos.Reposnum WHERE repos.reposart IN (";
      
                  int count = 0;
      
                  for (String anr : anrs){
                      selectSQL += "'"+anr+"'";
                      count++;
                      if (count < anrs.size()){
                          selectSQL += ",";
                      }
                  }
      
                  selectSQL += ")";
      

      那不好。但在这种情况下非常有效。

      谢谢。

      【讨论】:

      • 但是正如我之前提到的,你让自己对 SQL 注入持开放态度,或者这在你的情况下是不可能的?
      • 不,它不可能是一个没有任何外部输入的getter函数。没有用户输入只是选择所有项目。
      • 好的,如果你确定的话。但是我发现这种方法可以让跨站点脚本攻击者在您的数据库中放置坏东西,然后可以通过这样的代码射击您?!如果数据库上的值是自动生成的,那么你应该没问题,但是如果它们来自野外......你就有危险了!
      • 我同意@BillNaylor 的观点。由于它是您要附加到 select 语句的字符串列表,因此该列表中可能有任何内容。有人可以把"); DROP SCHEMA public CASCADE;"如果您没有仔细设置您的角色权限,您的整个数据库就会消失。也就是说,当然,如果 anrs 可以由用户以某种方式设置......
      猜你喜欢
      • 2020-06-06
      • 1970-01-01
      • 2014-03-12
      • 1970-01-01
      • 1970-01-01
      • 2015-03-16
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      相关资源
      最近更新 更多