Definindo um parâmetro como uma lista para uma expressão IN

Sempre que tento definir uma lista como um parâmetro para uso em uma expressão IN, recebo uma exceção de argumento Ilegal. Vários posts na internet parecem indicar que isso é possível, mas certamente não funciona para mim. Estou usando o Glassfish V2.1 com o Toplink.

Alguém mais foi capaz de fazer isso funcionar, se sim, como?

aqui está um código de exemplo:

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 a parte relevante do rastreamento de pilha:

 java.lang.IllegalArgumentException: Você tentou definir um valor do tipo class java.util.Arrays $ ArrayList para o parâmetro accountIds com o tipo esperado de class java.lang.Long da cadeia de caracteres de consulta SELECT a.accountManager.loginName FROM Account a WHERE a .id IN (: accountIds).
 em oracle.toplink.essentials.internal.ejb.cmp3.base.EJBQueryImpl.setParameterInternal (EJBQueryImpl.java:663)
 em oracle.toplink.essentials.internal.ejb.cmp3.EJBQueryImpl.setParameter (EJBQueryImpl.java:202)
 em com.corenap.newtDAO.ContactDaoBean.getNotificationAddresses (ContactDaoBean.java:437)
 em sun.reflect.NativeMethodAccessorImpl.invoke0 (método nativo)
 em sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39)
 em sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25)
 em java.lang.reflect.Method.invoke (Method.java:597)
 em com.sun.enterprise.security.application.EJBSecurityManager.runMethod (EJBSecurityManager.java:1011)
 em com.sun.enterprise.security.SecurityUtil.invoke (SecurityUtil.java:175)
 em com.sun.ejb.containers.BaseContainer.invokeTargetBeanMethod (BaseContainer.java:2920)
 em com.sun.ejb.containers.BaseContainer.intercept (BaseContainer.java:4011)
 em com.sun.ejb.containers.EJBObjectInvocationHandler.invoke (EJBObjectInvocationHandler.java:203)
 ... mais 67

Seu JPQL é inválido, remova os colchetes

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

Eu encontrei a resposta, fornecendo uma lista como um parâmetro não é suportado no JPA 1.0; no entanto, é suportado no JPA 2.0.

O provedor de persistência padrão para o Glassfish v2.1 é o Toplink, que implementa o JPA 1.0, para obter o JPA 2.0, é necessário o EclipseLink, que é o padrão para a visualização do Glassfish v3 ou pode ser conectado à versão 2.1.

Loren

Espero que isso ajude alguém. Eu enfrentei o problema e fiz o seguinte para resolver (usando eclipselink 2.2.0)

  1. Eu tinha o JavaEE jar e o jpa 2 jar (javax.persistence * 2 *) no caminho da class. Removido o JavaEE do caminho de class.

  2. Eu estava usando algo como " idItm IN ( :itemIds ) " que estava lançando a exceção:

tipo class java.util.ArrayList para o parâmetro itemIds com o tipo esperado de class java.lang.String da cadeia de consulta

Solução: acabei de alterar a condição para " idItm IN :itemIds " , ou seja, removi os colchetes ().

Simplesmente, o parâmetro será List e definido como

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

Isso funciona para o JPA 1.0

Ah, e se você não puder usar o EclipseLink por algum motivo, então aqui está um método que você pode usar para adicionar os bits necessários à sua consulta. Basta inserir a string resultante na sua consulta onde você colocaria “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; } 

Use NamedQuery em vez disso:

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

Adicione a consulta nomeada à sua entidade

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

Tente este código em vez daquele fornecido por @Szymon Tarnowski para adicionar a lista OR. Avisando se você tem centenas de IDs, você pode quebrar qualquer limite que esteja em vigor em relação ao comprimento máximo de uma consulta.

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

Para testar isso:

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

Que deu saída: AND (myfield = ‘122’ OU myfield = ‘132’ OU myfield = ‘112’)

Você também pode tentar esta 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 colocar em consulta, "Select * from Class where field in " + Class.generateCollection(list);