2010-02-17 9 views
16

Mi piace l'idea di Named Query in JPA per le query statiche che sto per eseguire, ma spesso desidero ottenere il risultato del conteggio per la query nonché un elenco di risultati da qualche sottoinsieme della query. Preferirei non scrivere due NamedQueries quasi identici. Idealmente, ciò che mi piacerebbe avere è qualcosa di simile:C'è un modo per ottenere le dimensioni del conteggio per una query con nome JPA con un set di risultati?

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

Quindi diciamo che m è 10, s è 0 e ci sono 400 righe in conto. Mi aspetto che r abbia una lista di 10 elementi, ma vorrei sapere che ci sono 400 righe in totale. Potrei scrivere un secondo @NamedQuery:

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

ma sembra una violazione DRY per farlo se sto sempre e solo andando a voler il conteggio. In questo semplice caso è facile mantenere i due sincronizzati, ma se la query cambia, sembra meno che ideale aggiornare entrambi @NamedQueries per mantenere i valori in linea.

Un caso di utilizzo comune in questo caso è il recupero di alcuni sottoinsiemi degli articoli, ma è necessario indicare il conteggio totale ("Visualizzazione 1-10 di 400").

risposta

13

Quindi la soluzione che ho usato è stata quella di creare due @NamedQuerys, uno per il set di risultati e uno per il conteggio, ma catturare la query di base in una stringa statica per mantenere DRY e assicurare che entrambe le query rimangano coerenti. Così, per quanto sopra, mi piacerebbe avere qualcosa di simile:

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

Ovviamente, con questo esempio, il corpo interrogazione è banale e questo è eccessivo. Ma con query molto più complesse, si finisce con una singola definizione del corpo della query e si può garantire che le due query siano sincronizzate. Si ottiene anche il vantaggio che le query sono precompilate e, almeno con Eclipselink, si ottiene la convalida all'avvio anziché quando si chiama la query.

Effettuando la denominazione coerente tra le due query, è possibile racchiudere il corpo del codice per eseguire entrambi i set basandosi semplicemente sul nome di base della query.

+0

finalmente funzionante anche per namedQueries! grazie – Zavael

+0

Interessante, non sapevo nemmeno che si potesse fare riferimento a finali statiche durante la configurazione delle annotazioni. Tuttavia, una soluzione che non richiedesse due query denominate separate sarebbe comunque l'ideale, credo. – aroth

5

Utilizzando setFirstResult/setMaxResults do non restituisce un sottoinsieme di un set di risultati, la query è nemmeno stato eseguito quando si chiama questi metodi, influenzano la query SELECT generato che verrà eseguita al momento della chiamata getResultList. Se si desidera ottenere il totale dei record, è necessario disporre delle SELECT COUNT entità in una query separata (in genere prima di impaginare).

Per un esempio completo, consultare Pagination of Data Sets in a Sample Application using JSF, Catalog Facade Stateless Session, and Java Persistence APIs.

+0

Sì. Voglio ottenere il conteggio completo del risultato della query. Pertanto, il conteggio dell'esempio non corrisponde a r.size() o lo utilizzerei semplicemente. Un potenziale caso d'uso è che voglio ottenere una pagina di risultati per l'elenco degli account, ma per consentire loro quante pagine totali, vorrei il conteggio totale dalla query, anche se sto recuperando solo 25 risultati. – Tim

+0

@Tim Ho chiarito la mia risposta. Se non è ancora chiaro, fammi sapere. –

+0

Grazie. Ho provato a chiarire la mia domanda. Fondamentalmente, so che i modificatori cambiano la query prima che venga eseguita. Quello che spero di fare è avere un singolo @NamedQuery che funge da base per la restituzione di un sottoinsieme così come ottenere un conteggio, invece di scrivere una query per il conteggio e una per il set di restituzione. Idealmente ancora usando @NamedQuery. Controllerò il link di riferimento. – Tim

1

vabbè è possibile utilizzare l'introspezione per ottenere query denominate annotazioni come:

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

Non so come questo si applica alla mia domanda. Voglio utilizzare NamedQuery per i vantaggi di precompilazione, memorizzazione nella cache e altre ottimizzazioni. Voglio che le due query siano identiche tranne che per un risultato restituito e uno che restituisce un conteggio. E voglio seguire DRY e non copiare e incollare la clausola condizionale. – Tim

Problemi correlati