Impostazione di un parametro come elenco per un’espressione IN

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)

  1. Ho avuto jar JavaEE e jar jpa 2 (javax.persistence * 2 *) nel percorso della class. Rimosso JavaEE dal percorso della class.

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