Установка параметра в виде списка для выражения 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

9 ответов

Решение

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

Поставщиком персистентности по умолчанию для Glassfish v2.1 является Toplink, который реализует JPA 1.0, чтобы получить JPA 2.0, вам необходим EclipseLink, который используется по умолчанию для предварительного просмотра Glassfish v3 или может быть подключен к v2.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();

Надеюсь, это поможет кому-то. Я столкнулся с проблемой и сделал следующее, чтобы решить (используя 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 "т.е. я снял скобки ().

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

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

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

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

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

Да, и если по какой-то причине вы не можете использовать 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;
    }

Попробуйте этот код вместо того, который предоставлен @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));
}

Который дал вывод: И ( myfield = '122' ИЛИ ​​myfield = '132' ИЛИ ​​myfield = '112')

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

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

Другие вопросы по тегам