2012-09-27 15 views
42

Me gustaría seleccionar solo columnas específicas (por ejemplo, SELECT a FROM b). Tengo un DAO genérico y lo que se me ocurrió es:JPA y API de criterios: seleccione solo columnas específicas

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

Y el error es: The method select(Selection<? extends T>) in the type CriteriaQuery<T> is not applicable for the arguments (Path<Object>) . ¿Cómo debería cambiar eso? Quiero obtener un objeto tipo T que tiene solo ID y VERSION campos, y todos los demás son null.

Tipo T extiende AbstractEntity que tiene esos 2 campos.

entityClazz es T.class.

Respuesta

58

Una de las formas JPA para obtener solo columnas concretas es solicitar un objeto Tuple.

En su caso, usted tendría que escribir algo como esto:

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

Otro enfoque es posible si usted tiene una clase que representa el resultado, como T en su caso. T no necesita ser una clase de Entidad. Si T tiene un constructor como:

public T(Long id, Long version) 

entonces se puede utilizar directamente en su TCriteriaQuery constructor:

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

ver este link para mayor referencia.

2

En primer lugar, realmente no veo por qué querría que un objeto solo tenga ID y Versión, y todos los demás accesorios sean nulos. Sin embargo, aquí hay un código que lo hará por usted (que no utiliza JPA Em, pero Hibernate normal. Supongo que puede encontrar la equivalencia en JPA o simplemente obtener el obj de sesión de Hibernate del delegado 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(); 

Esto devolverá una lista de objetos que tengan su ID y configurar la versión y todos los demás accesorios para nula, ya que el transformador aliasToBean no será capaz de encontrarlos. De nuevo, estoy seguro de que puedo pensar en una situación en la que me gustaría hacer eso.

+0

Gracias. Quiero usarlo en mi servicio web. El cliente solicita una lista de "algo" que solo contiene ID y versiones. Luego lo compara con el caché y solicita objetos completos que hayan cambiado. ¿Suena razonable? – BartoszCichecki

+0

¿Se trata de ahorrar ancho de banda de red? Depende de los datos, supongo, pero si espera que sus datos cambien a menudo, podría estar desperdiciando más ancho de banda debido a la sobrecarga de tcp/ip de crear dos solicitudes que a guardar simplemente enviando copias 'huecas'. Tal vez vale la pena echarle un vistazo? –

+1

Sí, lo sé. Voy a usarlo solo para datos que no cambiarán tan a menudo. – BartoszCichecki

Cuestiones relacionadas