2010-06-14 13 views
7

Uwielbiam bezpieczeństwo typu CriteriaQuery przynosząc JPA 2.0, ale przynosi również trochę kodu płyty kotła. Na przykład, powiedzmy, że mam encję o nazwie NamedEntity, która po prostu ma identyfikator i pole String o nazwie "name" (zakładam, że ma ono unikalne ograniczenie ustawione na true). Oto co NamedEntityManager może wyglądać następująco:Czy istnieje sposób na zmniejszenie ilości kodu płyty kotła powiązanego z CriteriaQuery (w 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(); 
    } 
} 

Czy istnieje sposób, aby skondensować kodu w celu uniknięcia kopiowanie/wklejanie samych linijek kodu do każdej metody wyszukiwania? Być może jakoś ponownie użyć obiektu CriteriaQuery?

+0

Ta sytuacja może być łatwo rozwiązana za pomocą strategii. Po prostu stwórz jedną prywatną metodę, która powinna przyjmować jeden parametr (interfejs) typu powiedz: WhereClauseBuilder, metoda prywatna otrzyma zmienną część (klauzula where) z tego parametru poprzez wywołanie metody, które przechodzi przez kryterium kryteria i zapytanie do niego. Wszystkie publiczne metody, po prostu wywołają prywatną metodę z konkretnym WhereClauseBuilder, który zwraca wymagany predykat gdzie klauzula. –

Odpowiedz

0

Wygląda na to, że nie ma sposobu na zmniejszenie ilości kodu. Chyba coś trzeba było poświęcić, aby uzyskać bezpieczeństwo typu.

4

Szukałem czegoś takiego, można spojrzeć na Querydsl (licencjonowane LGPL), które mogą mieć WZP jako backend.

Nadal czytam w nim, ale z ich przykładów wygląda całkiem nieźle.

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

Chociaż ta struktura nie jest powiązana z API Criteria i jest samodzielna, z pewnością warto się nią zająć. Dziękuję za wzmiankę o tym, zbadam to, gdy dostanę szansę! – Andrey

+0

Po prostu drobna korekta, Querydsl ma licencję LGPL, a nie licencję GPL. –

4

W JPA 2.1 najprawdopodobniej będzie możliwe połączenie JPQL i kryteriów. Przy takim podejściu można zdefiniować zapytanie bazowe za pomocą JPQL, a następnie użyć API Criteria do dynamicznego dodawania małych części.

Myślę, że API będzie wtedy mniej gadatliwe, ponieważ musisz użyć tylko jego niewielkich części.

+1

Nie jestem pewien, czy byłoby sens mieszać JPQL z zapytaniami Kryteriów. Włączenie JPQL byłoby sprzeczne z główną ideą kryjącą się za API Criteria - zapewnienie bezpieczeństwa typu kompilacji. – Andrey

+2

To oczywiście kompromis. Jest tak samo jak przy użyciu EL, aw niektórych przypadkach adnotacji. Zajmujesz się kompilacją czasu, aby zapewnić elastyczność i mniej szczegółowy kod. W każdym razie wiele osób uważa, że ​​to zmysły, ponieważ Linda (szefowa specyfikacji) poważnie rozważa to w przypadku JPA 2.1. Wybór należy do Ciebie: czysty JPQL, JPQL zmieszany z Kryteriami, a nawet w Kryteriach masz warianty bezpieczne dla typu i nietypowe. Użyj tego, co działa dla ciebie. –

0

Way przestarzały, ten post, ale chcę dodać to, co niedawno zbudowany dla prostych zapytań

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

Można go używać jak to

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

w końcu zatrzymany to. Głównie dlatego, że widziałem, że miałem tylko dwa zapytania i będę musiał przedłużyć DSL, aby uzyskać wymagane cechy zapytań do niego, takie jak

  • większa niż, mniej niż
  • wsparcia
  • Metamodel
  • QueryBuilder.currentDate() i zarówno.

Co więcej, uważam za brzydkie, aby zawsze dzwonić pod numer where, podczas gdy faktycznie odpowiada on bardziej SQLly and. W każdym razie, jeśli ktoś jest zainteresowany bardzo prostym interfejsem API zapytań, nadal warto spróbować.

BTW: Zapomnij o nazwach, to był prototyp, nic więcej.

Powiązane problemy