2009-10-12 14 views
20

Ilekroć próbuję ustawić listę jako parametr do użycia w wyrażeniu IN, otrzymuję niedozwolony argument wyjątku. Różne posty w Internecie zdają się wskazywać, że jest to możliwe, ale z pewnością nie działa to dla mnie. Używam Glassfish V2.1 z Toplink.Ustawianie parametru jako listy dla wyrażenia IN

Czy ktoś inny był w stanie to uruchomić, jeśli tak, w jaki sposób?

tutaj jakiś przykład kodu:

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

i odpowiednia część śladu stosu:

 
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 

Odpowiedz

18

Znalazłem odpowiedź, dostarczając listę jako parametr nie jest obsługiwana w JPA 1.0; jest jednak obsługiwany w JPA 2.0.

Domyślnym dostawcą trwałości dla Glassfish v2.1 jest Toplink, który implementuje JPA 1.0, aby uzyskać wersję JPA 2.0, potrzebny jest EclipseLink, który jest domyślny dla podglądu Glassfish v3 lub może być podłączony do wersji 2.1.

- Loren

+10

a JPQL jest źle, usuń nawiasy – James

+2

Zła. To zależy od implementacji z JPA 1.0 zaimplementowanej przez Hibernate, ale działa w postaci nawiasów. – Guaido79

+0

To był błąd w Hibernate: https://hibernate.atlassian.net/browse/HHH-5126 – Kawu

0

Aha, i jeśli nie można użyć EclipseLink z jakiegoś powodu, to tutaj jest metoda można użyć, aby dodać potrzebnych bitów do zapytania. Po prostu wstaw wynikowy ciąg do zapytania, w którym wstawisz "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

Mam nadzieję, że to pomoże. Mam w obliczu problemu i zrobił następujące czynności, aby rozwiązać (za pomocą EclipseLink 2.2.0)

  1. miałem JavaEE słoik jak również JPA 2 słoik (javax.persistence * 2 *) w ścieżce klasy. Usunięto środowisko JavaEE ze ścieżki klasy.

  2. używałem czegoś takiego " idItm IN (:itemIds) " który rzuca wyjątek:

typ zajęć java.util.ArrayList dla itemIds parametrów z oczekiwanymi typ klasy java.lang.String z łańcucha zapytania

Rozwiązanie: Właśnie zmieniłem stan na " idItm IN :itemIds ", tj. Usunąłem nawiasy klamrowe().

+0

wielkie dzięki @dillip zaoszczędziłeś mój dzień. Właśnie musiałem usunąć nawiasy wokół parametru – Sofiane

-3

Możesz także wypróbować tę składnię.

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

i umieścić w zapytaniu "Select * from Class where field in " + Class.generateCollection(list);

+2

Nie, nie, nie. Umożliwi to możliwości wstrzykiwania SQL. – siebz0r

37

Ty JPQL jest źle, usuń nawiasy

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

Niestety nie - miałem ten sam błąd .. mamy Javę EE 5, a więc EJB 3.0. Od specyfikacji: "JSR 220: Enterprise JavaBeans, wersja 3.0 - Java Persistence API" Sekcja Widzę, że wyrażenie "w" wymaga nawiasów klamrowych ... ale wtedy otrzymywałem IllegalArgumentException po ustawieniu parametru na listę .. więc jestem za pomocą strasznego hacka poniżej rozwijania identyfikatorów do łańcucha OR (dopóki nie będę mógł uzyskać odpowiednich odwzorowań JPA w miejscu z widokiem DB, aby tego uniknąć). –

+0

Kocham cię <3 <3 – Rob

+0

Nie działa dla mnie z .setParameter(), musiałem użyć setParameterList() – lukas84

0

Spróbuj kod zamiast jednej dostarczonych przez @Szymon Tarnowskiego dodać lub listy. ostrzeżenie jeśli masz setki identyfikatorów, możesz złamać wszelkie ograniczenia dotyczące maksymalnej długości zapytania.

/** 
* @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; 
} 

W tym teście:

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

co dało wynik: I (myfield = '122' lub myfield = '132' lub myfield = '112')

+7

Powtórz za mną: "Nigdy nie użyję StringBuilders ani innych podobnych technik do budowania zapytań SQL, ponieważ umożliwia to iniekcję SQL. użyje tylko przygotowanych oświadczeń i takich. " – siebz0r

+1

Dziękujemy za ostrzeżenie o potencjalnych lukach w SQL @ siebz0r. Jednak nie wierzę, że to jest ważne tutaj. Przygotowanych instrukcji nie można używać, ponieważ liczba argumentów nie jest znana, a wersja JPA nie zezwala na argumenty list. Także argumenty tutaj nie pochodzą z publicznych danych wejściowych i są rzutowane jako obiekty Integer. –

+0

Rozumiem, że ryzyko jest ograniczone, ale mimo to dziwne rzeczy mogą się zdarzyć. 'java.sql.PreparedStatement' może być używany w tak rzadkich przypadkach. Ten interfejs obsługuje kolekcje jako parametr. – siebz0r

1

prosto, parametr ten będzie Wykaz i ustawić go jako

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

działa to dla JPA 1.0

+0

to nie działa –

1

Zastosowanie NamedQuery zamiast:

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

Dodaj nazwie zapytanie do podmiotu

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