java jpa glassfish jpql toplink-essentials

java - Establecer un parámetro como una lista para una expresión IN



jpa glassfish (8)

Cada vez que intento establecer una lista como parámetro para usar en una expresión IN, obtengo una excepción de argumento Ilegal. Varias publicaciones en Internet parecen indicar que esto es posible, pero ciertamente no funciona para mí. Estoy usando Glassfish V2.1 con Toplink.

¿Alguien más ha podido hacer que esto funcione? Si es así, ¿cómo?

aquí hay un código de ejemplo:

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

y la parte relevante del seguimiento de la pila:

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


Ah, y si no puede usar EclipseLink por alguna razón, aquí hay un método que puede usar para agregar los bits necesarios a su consulta. Simplemente inserta la cadena resultante en tu consulta donde pondrías "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 &gt size; i++) { queryString = queryString.concat(" "+field+" = ''"+collection.get(i)+"''"); if(i &gt size-1) { queryString = queryString.concat(" OR"); } } queryString = queryString.concat(" )"); return queryString; }


Encontré la respuesta, proporcionando una lista ya que un parámetro no es compatible con JPA 1.0; sin embargo, es compatible con JPA 2.0.

El proveedor de persistencia predeterminado para Glassfish v2.1 es Toplink que implementa JPA 1.0, para obtener JPA 2.0 necesita EclipseLink, que es el predeterminado para la vista previa de Glassfish v3 o puede conectarse a v2.1.

- Loren


Espero que esto ayude a alguien. Me he enfrentado al problema e hice lo siguiente para resolverlo (usando eclipselink 2.2.0)

  1. Tenía JavaEE jar y jpa 2 jar (javax.persistence * 2 *) en la ruta de la clase. Se eliminó el JavaEE de la ruta de clase.

  2. Estaba usando algo así como " idItm IN ( :itemIds ) " que arrojaba la excepción:

Escriba la clase java.util.ArrayList para el elemento itemIds con el tipo esperado de la clase java.lang.String from query string

Solución: Acabo de cambiar la condición de " idItm IN :itemIds " , es decir, " idItm IN :itemIds " los corchetes ().


Pruebe este código en lugar del proporcionado por @Szymon Tarnowski para agregar la lista O. advertencia si tiene cientos de ID, sin embargo, puede romper el límite que esté en su lugar con respecto a la longitud máxima de una 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<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; }

Para probar esto:

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

Que dio salida: Y (myfield = ''122'' O myfield = ''132'' O myfield = ''112'')


Simplemente, el parámetro será List y lo establecerá como

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

Esto funciona para JPA 1.0


También puedes probar esta sintaxis.

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

Y poner en consulta, "Select * from Class where field in " + Class.generateCollection(list);


Tu JPQL no es válido, elimina los corchetes

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


Use NamedQuery en su lugar:

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

Agregue la consulta nombrada a su entidad

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