Установка параметра в виде списка для выражения IN

Всякий раз, когда я пытаюсь установить список в качестве параметра для использования в выражении IN, я получаю исключение недопустимого аргумента. Различные посты в Интернете, кажется, указывают на то, что это возможно, но это, конечно, не работает для меня. Я использую Glassfish V2.1 с Toplink.

Кто-нибудь еще смог заставить это работать, если да, то как?

вот пример кода:

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: вы попытались установить значение класса типа java.util.Arrays $ ArrayList для параметра accountIds с ожидаемым типом класса java.lang.Long из строки запроса. ВЫБЕРИТЕ a.accountManager.loginName ОТ УЧЕТА ГДЕ .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)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
at 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
12.10.2009 21:37:47
9 ОТВЕТОВ
РЕШЕНИЕ

Ваш 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();
39
23.03.2018 14:46:48
К сожалению нет - у меня была такая же ошибка .. у нас Java EE 5, поэтому EJB 3.0. Из спецификации: «JSR 220: Enterprise JavaBeans, версия 3.0 - API персистентности Java». Я вижу, что выражение «In» требует скобок ... но потом я получаю исключение IllegalArgumentException, когда задаю параметр в виде List <Integer> .. Я использую ужасный хак ниже для расширения идентификаторов в строку OR (пока я не смогу получить надлежащие отображения JPA на месте с помощью DB View, чтобы избежать этого).
Robert Mark Bram 25.07.2012 01:43:40
У меня не работает с .setParameter (), мне пришлось использовать setParameterList ()
lukas84 23.03.2017 10:45:49
Как строка var ids = "a1","b1",...;может быть передана в функцию Java и, в свою очередь, в оператор IN?
Venky 22.07.2017 13:50:56
@ lukas84 Это потому, что вы, скорее всего, используете hibernate Query, а не постоянство Query
Draken 28.09.2017 11:13:38

Я нашел ответ, предоставив список в качестве параметра, не поддерживается в JPA 1.0; однако это поддерживается в JPA 2.0.

Поставщиком персистентности по умолчанию для Glassfish v2.1 является Toplink, который реализует JPA 1.0, для получения JPA 2.0 необходим EclipseLink, который является значением по умолчанию для предварительного просмотра Glassfish v3 или может быть подключен к v2.1.

- Лорен

20
13.10.2009 16:48:40
Неправильно. Это зависит от реализации с JPA 1.0, реализованной Hibernate, но она требует скобок.
Guaido79 14.01.2015 18:11:40
Раньше это была ошибка в Hibernate: hibernate.atlassian.net/browse/HHH-5126
Kawu 30.05.2017 14:38:59

Да, и если вы не можете использовать 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 > size; i++) {
            queryString = queryString.concat(" "+field+" = '"+collection.get(i)+"'");
            if(i > size-1) {
                queryString = queryString.concat(" OR");
            }
        }
        queryString = queryString.concat(" )");
        return queryString;
    }
0
17.03.2010 20:39:25

Надеюсь, это кому-нибудь поможет. Я столкнулся с проблемой и сделал следующее, чтобы решить (используя eclipselink 2.2.0)

  1. У меня был JavaEE jar, а также jpa 2 jar (javax.persistence * 2 *) в пути к классам. Удален JavaEE из пути к классам.

  2. Я использовал что-то вроде того, " idItm IN ( :itemIds ) "который выбрасывал исключение:

класс типа java.util.ArrayList для параметра itemIds с ожидаемым типом класса java.lang.String из строки запроса

Решение: я просто изменил условие на " idItm IN :itemIds ", т.е. убрал скобки ().

10
28.09.2017 11:24:49
большое спасибо @dillip вы спасли мой день. Я просто должен был удалить скобки вокруг параметра
Sofiane 10.06.2016 13:48:57

Вы также можете попробовать этот синтаксис.

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);

-3
1.11.2011 13:37:22
Нет нет нет. Это позволит использовать возможности SQL-инъекций.
siebz0r 28.08.2012 05:58:08

Попробуйте этот код вместо того, который предоставлен @Szymon Tarnowski, чтобы добавить список OR. Предупреждение, если у вас есть сотни идентификаторов, вы можете нарушить любое ограничение в отношении максимальной длины запроса.

/**
 * @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' ИЛИ ​​myfield = '132' ИЛИ ​​myfield = '112')

-1
25.07.2012 01:33:51
Повторяйте за мной: «Я никогда не буду использовать StringBuilders или другие подобные методы для построения запросов SQL, поскольку это позволяет вводить SQL. Я буду использовать только подготовленные операторы и тому подобное».
siebz0r 28.08.2012 06:01:59
Спасибо за предупреждение о потенциальных уязвимостях в SQL @ siebz0r. Однако я не верю, что это действительно здесь. Подготовленные операторы нельзя использовать, так как количество аргументов неизвестно, а версия JPA не допускает список аргументов. Кроме того, аргументы здесь не из открытого ввода, и они приводятся как объекты Integer.
Robert Mark Bram 28.08.2012 21:08:24
Я понимаю, что риски ограничены, но все же могут происходить странные вещи. java.sql.PreparedStatementможно использовать для таких редких случаев. Этот интерфейс поддерживает коллекции в качестве параметра.
siebz0r 28.08.2012 21:39:51

Просто параметр будет List и установить его как

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

Это работает для JPA 1.0

3
25.07.2013 06:10:18

Вместо этого используйте 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")
0
30.11.2015 15:33:20

IN: идентификаторы вместо IN (: идентификаторы) - будут работать.

0
29.01.2019 11:02:53