2010-06-14 10 views
7

J'adore le type de sécurité CriteriaQuery qui apporte JPA 2.0 mais qui apporte aussi un peu de code de chaudière. Par exemple, disons que j'ai une entité appelée NamedEntity, qui a simplement un id et un champ String appelé "name" (supposons que la contrainte unique soit true). Voici ce que le NamedEntityManager pourrait ressembler à:Existe-t-il un moyen de réduire la quantité de code de plaque de chaudière associée à une requête CriteriaQuery (dans 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(); 
    } 
} 

est-il un moyen de condenser le code afin d'éviter de copier/coller les mêmes lignes de code dans chaque méthode de requête? Peut-être en quelque sorte réutiliser l'objet CriteriaQuery?

+0

Cette situation peut être facilement résolue en utilisant la stratégie. Il suffit de créer une méthode privée pour que cette méthode prenne un paramètre (une interface) de type dit, WhereClauseBuilder, la méthode privée obtiendra sa partie variable (where clause) de ce paramètre via un appel de méthode qui passe le criteriaBuilder et l'interroge. Toutes les méthodes publiques appellent simplement la méthode privée avec un WhereClauseBuilder spécifique qui renvoie la clause where de prédicat requise. –

Répondre

0

Il semble qu'il n'y ait aucun moyen de réduire la quantité de code. Je suppose que quelque chose a dû être sacrifié pour gagner la sécurité de type.

4

Je cherchais quelque chose comme ça, vous pouvez jeter un oeil à Querydsl (sous licence LGPL) qui peut avoir JPA comme backend. Je suis toujours en train de lire dessus, mais d'après leurs exemples, il semble plutôt propre.

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

Bien que ce framework ne soit pas lié à l'API Criteria et soit autonome, il vaut certainement la peine d'y jeter un coup d'œil. Merci d'avoir mentionné ceci, je vais l'explorer quand j'en aurai l'occasion! – Andrey

+0

Juste une correction mineure, Querydsl est sous licence LGPL, pas sous licence GPL. –

4

Dans JPA 2.1, il sera probablement possible de mélanger JPQL et les critères. Avec une telle approche, vous pouvez définir une requête de base avec JPQL, puis utiliser l'API Criteria pour ajouter dynamiquement de petites parties.

Je pense que l'API sera moins verbeuse, puisqu'il suffit d'en utiliser de petites parties.

+1

Je ne suis pas sûr s'il serait judicieux de mélanger JPQL avec des requêtes de critères. Inclure JPQL irait à l'encontre de l'idée principale derrière l'API Criteria - pour fournir une sécurité de type à la compilation. – Andrey

+2

C'est un compromis bien sûr. C'est juste comme en utilisant EL et dans certains cas, des annotations. Vous faites du trading en temps de sécurité pour plus de flexibilité et moins de verbosité. Quoi qu'il en soit, beaucoup de gens pensent que ça fait du sens car Linda (responsable de la spécification) envisage sérieusement cette possibilité pour JPA 2.1. Le choix est alors le vôtre: pur JPQL, JPQL mélangé avec des critères et même dans les critères, vous avez les variantes sécuritaires et non sécurisées. Utilisez ce qui fonctionne pour vous. –

0

Way pas à jour, ce poste, mais je veux ajouter ce que je récemment construit pour les requêtes simples

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

Vous pouvez l'utiliser comme ceci

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

En fin de compte je me suis arrêté cela. Principalement parce que je voyais que je n'avais que deux questions et je dois étendre le DSL pour obtenir les caractéristiques de requête nécessaires en elle, comme

  • supérieur, inférieur à
  • métamodèle soutien
  • QueryBuilder.currentDate() et ressemblent.

De plus, je trouve laid appeler toujours where alors qu'il correspond en fait à un plus SQLly and. Quoi qu'il en soit, si quelqu'un est intéressé par une API de requête très simple, cela vaut la peine d'essayer. BTW: Oubliez les noms, c'était un prototype, rien de plus.