2009-10-12 3 views
20

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

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

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

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: You have attempted to set a value of type class java.util.Arrays$ArrayList for parameter accountIds with expected type of class java.lang.Long from query string SELECT a.accountManager.loginName FROM Account a WHERE a.id IN (:accountIds). 
at oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.setParameterInternal(EJBQueryImpl.java:663) 
at oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.setParameter(EJBQueryImpl.java:202) 
at com.corenap.newtDAO.ContactDaoBean.getNotificationAddresses(ContactDaoBean.java:437) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
at java.lang.reflect.Method.invoke(Method.java:597) 
at com.sun.enterprise.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1011) 
at com.sun.enterprise.security.SecurityUtil.invoke(SecurityUtil.java:175) 
at com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod(BaseContainer.java:2920) 
at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4011) 
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:203) 
... 67 more 

ответ

18

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

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

- Loren

+10

и ваш JPQL неправильно, снять скобки – James

+2

Неправильно. Это зависит от реализации с JPA 1.0, реализованного Hibernate, он работает, но требует скобок. – Guaido79

+0

Это была ошибка в Hibernate: https://hibernate.atlassian.net/browse/HHH-5126 – Kawu

0

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

Надеюсь, это поможет кому-то. Я встал вопрос и сделал следующее разрешить (используя EclipseLink 2.2.0)

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

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

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

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

+0

спасибо большое @dillip, что вы спасли мой день. Мне просто нужно было удалить круглые скобки вокруг параметра – Sofiane

-3

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

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

+2

Нет, нет, нет. Это позволит возможности SQL-инъекций. – siebz0r

37

Вы 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(); 
+1

К сожалению, нет - у меня была такая же ошибка. У нас есть Java EE 5, поэтому EJB 3.0. Из спецификации: «JSR 220: Enterprise JavaBeans, версия 3.0 - Java Persistence API» Раздел «Я вижу, что« В »для выражения нужны скобки. Но тогда я получал исключение IllegalArgumentException, когда я устанавливал параметр как Список .. так что я используя ужасный взлом ниже расширения идентификаторов в строку OR (пока я не смогу получить правильные сопоставления JPA на месте с представлением DB, чтобы этого избежать). –

+0

Я люблю тебя <3 <3 – Rob

+0

Не работает для меня с .setParameter(), мне пришлось использовать setParameterList() – lukas84

0

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

/** 
* @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')

+7

Повторите после меня: «Я никогда не буду использовать StringBuilders или другие подобные методы для создания SQL-запросов, поскольку это позволяет SQL-инъекцию. будут использовать только подготовленные заявления и т. д. ». – siebz0r

+1

Благодарим вас за предупреждение о потенциальных уязвимостях с помощью SQL @ siebz0r. Однако я не считаю, что это справедливо здесь. Подготовленные утверждения не могут использоваться, поскольку количество аргументов неизвестно, а версия JPA не позволяет перечислять аргументы. Кроме того, аргументы здесь не от публичного ввода, и они передаются как объекты Integer. –

+0

Я понимаю, что риски ограничены, но все же могут произойти странные вещи. 'java.sql.PreparedStatement' может использоваться для таких редких случаев. Этот интерфейс поддерживает коллекции как параметр. – siebz0r

1

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

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

Это работает для JPA 1,0

+0

не работает –

1

Использование 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")