2010-06-14 23 views
7

Me encanta el tipo de seguridad CriteriaQuery que trae JPA 2.0 pero también trae un poco de código de placa de caldera. Por ejemplo, supongamos que tengo una entidad llamada NamedEntity, que simplemente tiene un id y un campo de cadena llamado "nombre" (suponiendo que tiene la restricción única establecida en verdadero). Esto es lo que la NamedEntityManager podría ser:¿Hay alguna forma de reducir la cantidad de código de placa de caldera asociada a un CriteriaQuery (en JPA 2.0)?

public class NamedEntityManager 
{ 
    //inject using your framework 
    EntityManager entityManager; 

    //retrieve all existing entities of type NamedEntity from DB 
    public Iterable<NamedEntity> queryAll() 
    { 
     CriteriaBuilder builder = entityManager.getCriteriaBuilder(); 
     CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class); 
     return entityManager.createQuery(query).getResultList(); 
    } 

    //retrieve a single entity of type NamedEntity from DB using specified name 
    public NamedEntity queryByName(String name) 
    { 
     CriteriaBuilder builder = entityManager.getCriteriaBuilder(); 
     CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class); 
     Root<NamedEntity> root = query.from(NamedEntity.class); 
     query = query.where(root.<NamedEntity>get("name").in(name)); 

     //skipped the try/catch block for the sake of brevity 
     return entityManager.createQuery(query).getSingleResult(); 
    } 
} 

¿Hay una manera de condensar el código con el fin de evitar copiar/pegar las mismas líneas de código en cada método de consulta? ¿Quizás reutilice de algún modo el objeto CriteriaQuery?

+0

Esta situación se puede abordar fácilmente mediante la estrategia. Simplemente cree un método privado que el método debe tomar un parámetro (una interfaz) de tipo say, WhereClauseBuilder, el método privado obtendrá su parte variable (cláusula where) de este parámetro a través de una llamada a método que pasa el criteriaBuilder y la consulta. Todos los métodos públicos, simplemente llamarán al método privado con un WhereClauseBuilder específico que devuelva la cláusula predicate where requerida. –

Respuesta

0

Parece que no hay forma de reducir la cantidad de código. Supongo que se debe sacrificar algo para ganar seguridad.

4

Estaba buscando algo así, podría echar un vistazo a Querydsl (licencia LGPL) que puede tener JPA como back-end.

Todavía estoy leyendo, pero por sus ejemplos, se ve bastante limpio.

HQLQuery q = new HibernateQuery(session); 
QCat cat = new QCat("cat"); // query type 
List<Cat> cats = q.from(cat).where(cat.name.between("A", "B")).list(cat); 
+0

Aunque este marco no está relacionado con la API de Criteria y es independiente, sin duda vale la pena investigarlo. ¡Gracias por mencionar esto, lo exploraré cuando tenga la oportunidad! – Andrey

+0

Solo una pequeña corrección, Querydsl tiene licencia LGPL, no licencia GPL. –

4

En JPA 2.1, lo más probable es que sea posible mezclar JPQL y Criterios. Con este enfoque, podría definir una consulta base con JPQL y luego usar la API Criteria para agregar partes pequeñas dinámicamente.

Me imagino que la API será menos detallada entonces, ya que solo necesita usar partes pequeñas de ella.

+1

No estoy seguro de si tendría sentido mezclar JPQL con Consultas de criterios. Incluir JPQL iría en contra de la idea principal detrás de Criteria API: proporcionar seguridad de tipo de tiempo de compilación. – Andrey

+2

Es una compensación, por supuesto. Es solo como usar EL y, en algunos casos, anotaciones. Está negociando seguridad en tiempo de compilación para obtener flexibilidad y un código menos detallado. De todos modos, mucha gente cree que tiene sentido, ya que Linda (líder de especificaciones) está considerando seriamente esto para JPA 2.1. La elección es entonces tuya: JPQL puro, JPQL mezclado con Criteria e incluso dentro de Criteria tienes las variantes de tipo seguro y no seguro de tipo. Usa lo que funciona para ti –

0

manera anticuada, este post, pero quiero añadir lo que de reciente construcción para consultas sencillas

public static class Jpa2Whatsoever { 

    private final EntityManager em; 

    public class Jpa2WhatsoeverProgress<T> { 

     private CriteriaQuery<T> cq; 
     private List<Predicate> predicates = new ArrayList<>(); 
     private Root<T> root; 

     public Jpa2WhatsoeverProgress(Class<T> type) { 
      this.cq = em.getCriteriaBuilder().createQuery(type); 
      this.root = cq.from(type); 

     } 

     public Jpa2WhatsoeverProgress<T> where(String attributeName, Object value) { 

      Predicate equal = em.getCriteriaBuilder().equal(root.get(attributeName), value); 

      predicates.add(equal); 
      return this; 
     } 

     public List<T> getResultList() { 
      Predicate[] predicatesArray = new Predicate[predicates.size()]; 
      TypedQuery<T> typedQuery = em.createQuery(cq.select(root).where(predicates.toArray(predicatesArray))); 

      List<T> resultList = typedQuery.getResultList(); 

      return Collections.unmodifiableList(resultList); 
     } 

    } 

    public Jpa2Whatsoever(EntityManager entityManager) { 
     this.em = entityManager; 
    } 

    public <T> Jpa2WhatsoeverProgress<T> select(Class<T> type) { 
     return new Jpa2WhatsoeverProgress<T>(type); 
    } 
} 

Puede utilizar de esta manera

List<MyEntity> matchingEntities = new Jpa2Whatsoever(entityManager).select(MyEntity.class).where("id", id).where("due", new Date()).getResultList(); 

Al final dejé de esto. Principalmente porque vi que tenía sólo dos consultas y que tendría que extender el DSL para obtener las características de consulta requeridos en ella, tales como

  • mayor que, menor que
  • apoyo
  • Metamodel
  • QueryBuilder.currentDate() y igual.

Además, creo que es feo para siempre llame where mientras que en realidad corresponde a una más SQLly and. De todos modos, si alguien está interesado en una API de consulta muy simple, vale la pena intentarlo.

BTW: Olvídese de los nombres, este era un prototipo, nada más.

Cuestiones relacionadas