Ogni volta che provo a impostare un elenco come parametro da utilizzare in un’espressione IN ottengo un’eccezione argomento non valido. Vari post su internet sembrano indicare che ciò è ansible, ma sicuramente non funziona per me. Sto usando Glassfish V2.1 con Toplink.
Qualcun altro è stato in grado di farlo funzionare, se sì come?
ecco alcuni esempi di codice:
List 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();
e la parte rilevante della traccia dello stack:
java.lang.IllegalArgumentException: Si è tentato di impostare un valore di tipo class java.util.Arrays $ ArrayList per parametro accountIds con tipo di class previsto java.lang.Long da query string SELECT a.accountManager.loginName FROM Account a WHERE a .id IN (: accountIds). a oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.setParameterInternal (EJBQueryImpl.java:663) a oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.setParameter (EJBQueryImpl.java:202) a com.corenap.newtDAO.ContactBeachBean.getNotificationAddresses (ContactDaoBean.java:437) a sun.reflect.NativeMethodAccessorImpl.invoke0 (metodo nativo) a sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39) a sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25) a java.lang.reflect.Method.invoke (Method.java:597) a com.sun.enterprise.security.application.EJBSecurityManager.runMethod (EJBSecurityManager.java:1011) a com.sun.enterprise.security.SecurityUtil.invoke (SecurityUtil.java:175) a com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod (BaseContainer.java:2920) a com.sun.ejb.containers.BaseContainer.intercept (BaseContainer.java:4011) a com.sun.ejb.containers.EJBObjectInvocationHandler.invoke (EJBObjectInvocationHandler.java:203) ... 67 di più
Il tuo JPQL non è valido, rimuovi le parentesi
List 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();
Ho trovato la risposta, fornendo un elenco come parametro non supportato in JPA 1.0; tuttavia, è supportato in JPA 2.0.
Il provider di persistenza predefinito per Glassfish v2.1 è Toplink che implementa JPA 1.0, per ottenere JPA 2.0 è necessario EclipseLink, che è l’impostazione predefinita per l’anteprima di Glassfish v3 o può essere inserita nella v2.1.
– Loren
Spero che questo aiuti qualcuno. Ho affrontato il problema e ho fatto quanto segue per risolvere (usando eclipselink 2.2.0)
Ho avuto jar JavaEE e jar jpa 2 (javax.persistence * 2 *) nel percorso della class. Rimosso JavaEE dal percorso della class.
Stavo usando qualcosa come " idItm IN ( :itemIds ) "
che stava lanciando l’eccezione:
type class java.util.ArrayList per parametro itemIds con tipo di class previsto java.lang.String dalla stringa di query
Soluzione: Ho appena modificato la condizione in " idItm IN :itemIds "
, ovvero ho rimosso le parentesi ().
Semplicemente, il parametro sarà List e lo imposterà come
"...WHERE a.id IN (:ids)") .setParameter("ids", yourlist)
Questo funziona per JPA 1.0
Oh, e se non è ansible utilizzare EclipseLink per qualche motivo, ecco un metodo che è ansible utilizzare per aggiungere i bit necessari alla query. Basta inserire la stringa risultante nella query in cui inserire “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; }
Utilizzare invece NamedQuery:
List logins = em.createNamedQuery("Account.findByIdList").setParameter("ids", Arrays.asList(new Long(1000100), new Long(1000110))).getResultList();
Aggiungi la query nominata alla tua quadro
@NamedQuery(name = "Account.findByIdList", query = "SELECT a.accountManager.loginName FROM Account a WHERE a.id IN :ids")
Prova questo codice al posto di quello fornito da @Szymon Tarnowski per aggiungere la lista OR .. avvertendo se hai centinaia di ID, potresti rompere qualunque limite sia presente per quanto riguarda la lunghezza massima di una query.
/** * @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 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; }
Per testare questo:
public static void main(String[] args) { ArrayList list = new ArrayList (); list.add(122); list.add(132); list.add(112); System.out.println(in("myfield", list)); }
Che ha dato l’output: AND (myfield = ‘122’ OR myfield = ‘132’ OR myfield = ‘112’)
Puoi anche provare questa syntax.
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; }
E metti nella query "Select * from Class where field in " + Class.generateCollection(list);