2012-09-27 15 views
42

Vorrei selezionare solo colonne specifiche (ad esempio SELECT a FROM b). Ho un DAO generico e quello che mi è venuto in mente è:API JPA e criteri - Selezionare solo colonne specifiche

public List<T> getAll(boolean idAndVersionOnly) { 
    CriteriaBuilder builder = manager.getCriteriaBuilder(); 
    CriteriaQuery<T> criteria = builder.createQuery(entityClazz); 
    Root<T> root = criteria.from(entityClazz); 
    if (idAndVersionOnly) { 
     criteria.select(root.get("ID").get("VERSION")); // HERE IS ERROR 
    } else { 
     criteria.select(root); 
    } 
    return manager.createQuery(criteria).getResultList(); 
} 

e l'errore viene: The method select(Selection<? extends T>) in the type CriteriaQuery<T> is not applicable for the arguments (Path<Object>). Come dovrei cambiarlo? Voglio ottenere un oggetto tipo T con solo campi ID e VERSION e tutti gli altri sono null.

Tipo T estende AbstractEntity che ha quei 2 campi.

entityClazz è T.class.

risposta

58

Uno dei modi JPA per ottenere solo colonne particolari è richiedere un oggetto Tuple.

Nel tuo caso si avrebbe bisogno di scrivere qualcosa del genere:

CriteriaQuery<Tuple> cq = builder.createTupleQuery(); 
// write the Root, Path elements as usual 
Root<EntityClazz> root = cq.from(EntityClazz.class); 
cq.multiselect(root.get(EntityClazz_.ID), root.get(EntityClazz_.VERSION)); //using metamodel 
List<Tuple> tupleResult = em.createQuery(cq).getResultList(); 
for (Tuple t : tupleResult) { 
    Long id = (Long) t.get(0); 
    Long version = (Long) t.get(1); 
} 

Un altro approccio è possibile se si dispone di una classe che rappresenta il risultato, come T nel tuo caso. T non ha bisogno di essere una classe di entità. Se T ha un costruttore come:

public T(Long id, Long version) 

quindi è possibile utilizzare T direttamente in CriteriaQuery costruttore:

CriteriaQuery<T> cq = builder.createQuery(T.class); 
// write the Root, Path elements as usual 
Root<EntityClazz> root = cq.from(EntityClazz.class); 
cq.multiselect(root.get(EntityClazz_.ID), root.get(EntityClazz_.VERSION)); //using metamodel 
List<T> result = em.createQuery(cq).getResultList(); 

Vai a questa link per ulteriore riferimento.

2

Prima di tutto, non vedo davvero perché vorreste che un oggetto avesse solo ID e versione, e tutti gli altri oggetti di scena fossero nulli. Tuttavia, ecco un codice che lo farà per te (che non usa JPA Em, ma normale Hibernate. Suppongo tu possa trovare l'equivalenza in JPA o semplicemente ottenere l'Hibernate Session obj dal delegato em Accessing Hibernate Session from EJB using EntityManager ):

List<T> results = session.createCriteria(entityClazz) 
    .setProjection(Projections.projectionList() 
     .add(Property.forName("ID")) 
     .add(Property.forName("VERSION")) 
    ) 
    .setResultTransformer(Transformers.aliasToBean(entityClazz); 
    .list(); 

Questo restituirà un elenco di oggetti che hanno la loro identità e impostare la versione e tutti gli altri oggetti di scena a NULL, come il trasformatore aliasToBean non sarà in grado di trovarli. Ancora una volta, sono incerto di poter pensare a una situazione in cui vorrei farlo.

+0

Grazie. Voglio usarlo nel mio webservice. Il client richiede un elenco di "qualcosa" contenente solo ID e versioni. Quindi lo confronta con la cache e richiede gli oggetti completi modificati. Sembra ragionevole? – BartoszCichecki

+0

Consente di risparmiare larghezza di banda della rete? Dipendo dai dati, presumo, ma se ti aspetti che i tuoi dati cambino spesso, potresti perdere più larghezza di banda a causa del sovraccarico di tcp/ip nella creazione di due richieste rispetto al salvataggio di solo copie "vuote". Forse vale la pena di provarlo? –

+1

Sì, lo so. Lo userò solo per i dati che non cambieranno così spesso. – BartoszCichecki

Problemi correlati