【问题标题】:Setting a parameter as a list for an IN expression将参数设置为 IN 表达式的列表
【发布时间】:2010-12-06 03:19:01
【问题描述】:

每当我尝试将列表设置为在 IN 表达式中使用的参数时,都会出现非法参数异常。互联网上的各种帖子似乎表明这是可能的,但它肯定不适合我。我正在使用带有 Toplink 的 Glassfish V2.1。

有没有其他人能够让这个工作,如果可以,如何?

这里有一些示例代码:

List<String> logins = em.createQuery("SELECT a.accountManager.loginName " +
    "FROM Account a " +
    "WHERE a.id IN (:ids)")
    .setParameter("ids",Arrays.asList(new Long(1000100), new Long(1000110)))
    .getResultList();

以及堆栈跟踪的相关部分:

java.lang.IllegalArgumentException:您试图从查询字符串 SELECT a.accountManager.loginName FROM Account a WHERE a 中为参数 accountIds 设置类型为 class java.util.Arrays$ArrayList 的值,预期类型为 java.lang.Long .id IN (:accountIds)。 在 oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.setParameterInternal(EJBQueryImpl.java:663) 在 oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.setParameter(EJBQueryImpl.java:202) 在 com.corenap.newtDAO.ContactDaoBean.getNotificationAddresses(ContactDaoBean.java:437) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 在 java.lang.reflect.Method.invoke(Method.java:597) 在 com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011) 在 com.sun.enterprise.security.SecurityUtil.invoke(SecurityUtil.java:175) 在 com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod(BaseContainer.java:2920) 在 com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4011) 在 com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:203) ... 67 更多

【问题讨论】:

    标签: java jpa glassfish jpql toplink-essentials


    【解决方案1】:

    您的 JPQL 无效,请去掉括号

    List<String> logins = em.createQuery("SELECT a.accountManager.loginName " +
        "FROM Account a " +
        "WHERE a.id IN :ids")
        .setParameter("ids",Arrays.asList(new Long(1000100), new Long(1000110)))
        .getResultList();
    

    【讨论】:

    • 很遗憾没有 - 我遇到了同样的错误。我们有 Java EE 5,因此有 EJB 3.0。从规范:“JSR 220:Enterprise JavaBeans,版本 3.0 - Java Persistence API”部分我看到“In”表达式需要括号......但是当我将参数设置为 List 时,我得到了 IllegalArgumentException ......所以我正在使用下面的可怕技巧,将 ID 扩展为一串 OR(直到我可以使用 DB 视图获得正确的 JPA 映射以避免这种情况)。
    • .setParameter() 对我不起作用,我必须使用 setParameterList()
    • 如何将var ids = "a1","b1",...; 之类的字符串传递给java 函数,然后再传递给IN 运算符?
    • @lukas84 那是因为你最有可能使用hibernate Query 而不是persistence Query
    【解决方案2】:

    我找到了答案,JPA 1.0 不支持提供列表作为参数;但是,它在 JPA 2.0 中受支持。

    Glassfish v2.1 的默认持久性提供程序是实现 JPA 1.0 的 Toplink,要获得 JPA 2.0,您需要 EclipseLink,它是 Glassfish v3 预览版的默认设置,或者可以插入 v2.1。

    - 罗兰

    【讨论】:

    【解决方案3】:

    希望这对某人有所帮助。我遇到了这个问题并做了以下解决(使用eclipselink 2.2.0)

    1. 我在类路径中有 JavaEE jar 和 jpa 2 jar(javax.persistence*2*)。从类路径中删除了 JavaEE。

    2. 我正在使用类似" idItm IN ( :itemIds ) " 的东西抛出异常:

    为具有预期的参数 itemIds 键入类 java.util.ArrayList 来自查询字符串的类 java.lang.String 的类型

    解决方法:我只是将in条件改为" idItm IN :itemIds ",即去掉了括号()。

    【讨论】:

    • 非常感谢@dillip,你拯救了我的一天。我只需要删除参数周围的括号
    【解决方案4】:

    简单来说,参数就是List,设置成

    "...WHERE a.id IN (:ids)")
    .setParameter("ids", yourlist)
    

    这适用于 JPA 1.0

    【讨论】:

      【解决方案5】:

      IN :ids 而不是 IN (:ids) - 会起作用。

      【讨论】:

        【解决方案6】:

        改用 NamedQuery:

        List<String> logins = em.createNamedQuery("Account.findByIdList").setParameter("ids", Arrays.asList(new Long(1000100), new Long(1000110))).getResultList();
        

        将命名查询添加到您的实体

        @NamedQuery(name = "Account.findByIdList", query = "SELECT a.accountManager.loginName FROM Account a WHERE a.id IN :ids")
        

        【讨论】:

          【解决方案7】:

          哦,如果由于某种原因您不能使用 EclipseLink,那么您可以使用以下方法将所需的位添加到您的查询中。只需将生成的字符串插入到您将放置“a.id IN (:ids)”的查询中。

              /** 
               * @param field The jpql notation for the field you want to evaluate
               * @param collection The collection of objects you want to test against
               * @return Jpql that can be concatenated into a query to test if a feild is in a collection of objects
               */
              public String in(String field, List collection) {
                  String queryString = new String();
                  queryString = queryString.concat(" AND (");
                  int size = collection.size();
                  for(int i = 0; i &gt size; i++) {
                      queryString = queryString.concat(" "+field+" = '"+collection.get(i)+"'");
                      if(i &gt size-1) {
                          queryString = queryString.concat(" OR");
                      }
                  }
                  queryString = queryString.concat(" )");
                  return queryString;
              }
          

          【讨论】:

            【解决方案8】:

            尝试使用此代码而不是 @Szymon Tarnowski 提供的代码来添加 OR 列表。警告如果您有数百个 ID,您可能会打破关于最大长度的任何限制查询。

            /**
             * @param field
             *           The jpql notation for the field you want to evaluate
             * @param collection
             *           The collection of objects you want to test against
             * @return Jpql that can be concatenated into a query to test if a feild is
             *         in a collection of objects
             */
            public static String in(String field, List<Integer> idList) {
              StringBuilder sb = new StringBuilder();
              sb.append(" AND (");
              for(Integer id : idList) {
                sb.append(" ").append(field).append(" = '").append(id).append("'").append(" OR ");
              }
              String result = sb.toString();
              result = result.substring(0, result.length() - 4); // Remove last OR
              result += ")";
              return result;
            }
            

            对此进行测试:

            public static void main(String[] args) {
              ArrayList<Integer> list = new ArrayList<Integer>();
              list.add(122);
              list.add(132);
              list.add(112);
              System.out.println(in("myfield", list));
            }
            

            这给出了输出: AND (myfield = '122' OR myfield = '132' OR myfield = '112')

            【讨论】:

            • 在我之后重复:“我永远不会使用 StringBuilders 或其他类似技术来构建 SQL 查询,因为这会启用 SQL 注入。我只会使用准备好的语句等。”
            • 感谢您就 SQL @siebz0r 的潜在漏洞发出警告。但是我不相信这在这里有效。无法使用准备好的语句,因为 args 的数量未知并且 JPA 的版本不允许列出 args。此外,这里的参数不是来自公共输入,它们被转换为 Integer 对象。
            • 我知道风险是有限的,但仍然会发生奇怪的事情。 java.sql.PreparedStatement 可以用于这种罕见的情况。该接口支持集合作为参数。
            【解决方案9】:

            你也可以试试这个语法。

            static public String generateCollection(List list){
                if(list == null || list.isEmpty())
                    return "()";
                String result = "( ";
                for(Iterator it = list.iterator();it.hasNext();){
                    Object ob = it.next();
                    result += ob.toString();
                    if(it.hasNext())
                        result += " , ";
                }
                result += " )";
                return result;
            }
            

            并查询,"Select * from Class where field in " + Class.generateCollection(list);

            【讨论】:

            • 不,不,不。这将使 SQL 注入成为可能。
            猜你喜欢
            • 2012-05-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-03-03
            • 2015-11-15
            • 1970-01-01
            • 2015-06-09
            • 1970-01-01
            相关资源
            最近更新 更多