2010-02-17 16 views
16

Me gusta la idea de las Consultas con nombre en JPA para las consultas estáticas que voy a hacer, pero a menudo quiero obtener el resultado del conteo para la consulta, así como una lista de resultados de algún subconjunto de la consulta. Prefiero no escribir dos NamedQueries casi idénticas. Idealmente, lo que me gustaría tener es algo así como:¿Hay alguna manera de obtener el tamaño de conteo para una consulta con nombre JPA con un conjunto de resultados?

@NamedQuery(name = "getAccounts", query = "SELECT a FROM Account") 
. 
. 
    Query q = em.createNamedQuery("getAccounts"); 
    List r = q.setFirstResult(s).setMaxResults(m).getResultList(); 
    int count = q.getCount(); 

Así que digamos que m es 10, s es 0 y hay 400 filas en cuenta. Esperaría tener una lista de 10 elementos, pero me gustaría saber que hay 400 filas en total. Podría escribir un segundo @NamedQuery:

@NamedQuery(name = "getAccountCount", query = "SELECT COUNT(a) FROM Account") 

pero parece una violación DRY para hacer eso si estoy siempre solo va a querer el recuento. En este caso simple, es fácil mantener los dos sincronizados, pero si la consulta cambia, parece menos que ideal que tenga que actualizar ambos @NamedQueries para mantener los valores en línea.

Un caso de uso común aquí sería buscar algún subconjunto de los elementos, pero que necesitan alguna forma de indicar el recuento total ("Mostrando 1-10 de 400").

Respuesta

13

Así que la solución que terminé usando fue crear dos @NamedQuerys, uno para el conjunto de resultados y otro para el conteo, pero capturando la consulta base en una cadena estática para mantener DRY y asegurar que ambas consultas permanezcan consistentes. Así que por lo anterior, tendría algo así como:

@NamedQuery(name = "getAccounts", query = "SELECT a" + accountQuery) 
@NamedQuery(name = "getAccounts.count", query = "SELECT COUNT(a)" + accountQuery) 
. 
static final String accountQuery = " FROM Account"; 
. 
    Query q = em.createNamedQuery("getAccounts"); 
    List r = q.setFirstResult(s).setMaxResults(m).getResultList(); 
    int count = ((Long)em.createNamedQuery("getAccounts.count").getSingleResult()).intValue(); 

Obviamente, con este ejemplo, el cuerpo de la consulta es trivial y esto es una exageración. Pero con consultas mucho más complejas, terminas con una sola definición del cuerpo de la consulta y puedes asegurarte de tener las dos consultas sincronizadas. También tiene la ventaja de que las consultas están precompiladas y, al menos con Eclipselink, obtiene la validación en el momento del inicio en lugar de cuando llama a la consulta.

Al hacer un nombre consistente entre las dos consultas, es posible ajustar el cuerpo del código para ejecutar ambos conjuntos simplemente basando el nombre base de la consulta.

+0

finalmente resolviendo la solución para namedQueries también! gracias – Zavael

+0

Interesante, ni siquiera sabía que podía hacer referencia a finales estáticos cuando configuraba anotaciones. Sin embargo, creo que una solución que no requiera dos consultas específicas separadas sería ideal. – aroth

5

Usando setFirstResult/setMaxResults hacer no devuelven un subconjunto de un conjunto de resultados, la consulta ni siquiera se ha ejecutar cuando se llama a estos métodos, que afectan a la consulta SELECT generada que se ejecutará cuando se llama a getResultList. Si desea obtener el recuento total de registros, tendrá que SELECT COUNT sus entidades en una consulta separada (generalmente antes de paginar).

Para obtener un ejemplo completo, consulte Pagination of Data Sets in a Sample Application using JSF, Catalog Facade Stateless Session, and Java Persistence APIs.

+0

Sí. Quiero obtener el recuento completo de la consulta. Así que contar en el ejemplo no correspondería a r.size() o simplemente usaría eso. Un posible caso de uso es que quiero obtener una página de resultados para listar cuentas, pero para dejarles el número de páginas en total, me gustaría el recuento total de la consulta, aunque solo estoy obteniendo 25 resultados. – Tim

+0

@Tim He aclarado mi respuesta. Si aún no está claro, házmelo saber. –

+0

Gracias. Intenté aclarar mi pregunta. Básicamente, sé que los modificadores cambian la consulta antes de que se ejecute. Lo que espero hacer es tener una sola @NamedQuery que sirva como base para devolver un subconjunto y obtener un recuento, en lugar de escribir una consulta para el conteo y otra para el conjunto de resultados. Lo ideal es seguir usando @NamedQuery. Verificare el enlace al que hace referencia. – Tim

1

oh bien puede utilizar la introspección para obtener consultas con nombre anotaciones como:

String getNamedQueryCode(Class<? extends Object> clazz, String namedQueryKey) { 
    NamedQueries namedQueriesAnnotation = clazz.getAnnotation(NamedQueries.class); 
    NamedQuery[] namedQueryAnnotations = namedQueriesAnnotation.value(); 

    String code = null; 
    for (NamedQuery namedQuery : namedQueryAnnotations) { 
     if (namedQuery.name().equals(namedQueryKey)) { 
      code = namedQuery.query(); 
      break; 
     } 
    } 

    if (code == null) { 
     if (clazz.getSuperclass().getAnnotation(MappedSuperclass.class) != null) { 
      code = getNamedQueryCode(clazz.getSuperclass(), namedQueryKey); 
     } 
    } 

    //if not found 
    return code; 
} 
+0

No estoy seguro de cómo esto se aplica a mi pregunta. Quiero utilizar NamedQuery para las ventajas de precompilación, almacenamiento en caché y otras optimizaciones. Quiero que las dos consultas sean idénticas, excepto por un resultado que se devuelve y otro que devuelve un conteo. Y quiero seguir DRY y no copiar y pegar la cláusula condicional. – Tim

Cuestiones relacionadas