2015-07-29 2 views
1

У меня есть следующий фрагмент кода для строителя критериев построения, где условие.JPA Criteria Predicate Conditions

Хотелось бы знать, есть ли способы сделать это лучше, так как у меня будет больше условий, при которых будут использоваться условия и одинаковые условия для получения количества записей.

Любое понимание является весьма заметным

private List <Product> getProducts(MultivaluedMap params) throws JSONException { 

    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder(); 
    CriteriaQuery <Product> criteriaQuery = criteriaBuilder.createQuery(Product.class); 

    Root <Product> root = criteriaQuery.from(Product.class); 

    List <Predicate> p = new ArrayList <Predicate>(); 
    Predicate prodIdPredicate, prodNamePredicate; 

    JSONObject inputJSON = new JSONObject(params); 

    if (inputJSON.isNull("filter") == false) { 
     JSONObject filter = inputJSON.getJSONObject("filter"); 
     JSONArray filters = filter.getJSONArray("filters"); 
     for (int i = 0; i < filters.length(); i++) { 

      JSONObject j = (JSONObject) filters.get(i); 
      if (j.getString("field").equals("prodId")) { 
       prodIdPredicate = criteriaBuilder.like(root.get(Product_.prodId), j.getString("value")); 
       p.add(prodIdPredicate); 
      } 

      if (j.getString("field").equals("prodName")) { 
       prodNamePredicate = criteriaBuilder.like(root.get(Product_.prodName), j.getString("value")); 
       p.add(prodNamePredicate); 
      } 

     } 
    } 
    Predicate[] pr = new Predicate[p.size()]; 
    p.toArray(pr); 
    criteriaQuery.where(pr);  

ответ

3

Прежде всего, вы должны рассмотреть вопрос о реструктуризации вашего приложения в виде слоев. Вам, по крайней мере, нужен 3 уровня, DAO, Service и WebService.

Все, что касается базы данных и JPA, должно быть в вашем слое DAO. И все связанные с json вещи должны быть на вашем уровне WebService. Уровень обслуживания должен управлять транзакцией и связью между веб-сервисом и уровнем dao.

Сначала давайте поговорим о вашем уровне веб-службы. Ваши объекты JSON, вероятно, поступают из веб-службы Restful. Поскольку почти все фреймворки поддерживают json marshalling/unmarshalling, нецелесообразно вручную анализировать объекты передачи данных. Под этим я подразумеваю, вы можете предпочесть объявить класс FieldDto и передать его экземпляры вместо JSONObject. Вот пример FieldDto. Это ПОЖО.

public class FieldDto { 
    private String prodId; 
    private String prodName; 
    // Getters & Setters etc. 
} 

Вы можете легко вывести/развязать json с помощью GSON или Jackson. Вероятно, ваша инфраструктура имеет один из них по умолчанию для обработки json-преобразования.

Следующий слой - это служебный уровень. На уровне обслуживания вы управляете своими транзакциями и конвертируете объекты DTO в то, что может легко понять ваш уровень DAO. В этом случае ваш уровень обслуживания пройдет fieldDto.getProdId() и fielDto.getProdName() на уровень DAO.

Ваш последний слой - слой DAO. Сначала давайте изменим вашу подпись метода.

public List <Product> getProducts(String prodId, String prodName) { 

    CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder(); 
    CriteriaQuery <Product> criteriaQuery = criteriaBuilder.createQuery(Product.class); 

    Root <Product> root = criteriaQuery.from(Product.class); 

    List <Predicate> p = new ArrayList <Predicate>(); 

    if(prodId != null){ 
     p.add(criteriaBuilder.like(root.get(Product_.prodId),prodId)); 
    } 

    if(prodName != null){ 
     p.add(criteriaBuilder.like(root.get(Product_.prodName), prodName)); 
    } 

    if(!p.isEmpty()){ 
     Predicate[] pr = new Predicate[p.size()]; 
     p.toArray(pr); 
     criteriaQuery.where(pr);  
    } 
    return getEntityManager().createQuery(criteriaQuery).getResultList(); 
} 

Это не оно. Этот код по-прежнему нуждается в улучшении. В одном из моих проектов я создаю свободный api для управления всеми деталями шаблонов. Когда вы начнете писать другие классы DAO, вы поймете, что некоторые из блоков кода повторяются снова и снова.

Ниже приведен пример свободного api. Вы можете создать свою версию.

import javax.persistence.EntityManager; 
import javax.persistence.LockModeType; 
import javax.persistence.PersistenceException; 
import javax.persistence.TypedQuery; 
import javax.persistence.criteria.*; 
import javax.persistence.metamodel.Attribute; 
import javax.persistence.metamodel.CollectionAttribute; 
import javax.persistence.metamodel.PluralAttribute; 
import javax.persistence.metamodel.SingularAttribute; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.List; 
import java.util.Vector; 

public final class SimpleSelectBuilder<E extends Entity> { 

    private final EntityManager entityManager; 
    private final CriteriaBuilder criteriaBuilder; 
    private final CriteriaQuery<E> criteriaQuery; 
    private final Root<E> root; 
    private final Collection<Predicate> predicates; 

    private Integer first = null; 
    private Integer max = null; 
    private LockModeType lockModeType = null; 

    public SimpleSelectBuilder(final EntityManager entityManager, final Class<E> entityClazz) { 
     this.entityManager = entityManager; 
     this.criteriaBuilder = entityManager.getCriteriaBuilder(); 
     this.criteriaQuery = this.criteriaBuilder.createQuery(entityClazz); 
     this.root = criteriaQuery.from(entityClazz); 
     this.predicates = new Vector<>(); 
    } 

    public SimpleSelectBuilder<E> and(final Attribute attribute, final Object value) { 
     final Expression expression = this.getExpression(attribute, root); 
     this.predicates.add(criteriaBuilder.equal(expression, value)); 
     return this; 
    } 

    public SimpleSelectBuilder<E> andNotIn(final Attribute attribute, final Collection<Object> values) { 
     final Expression expression = this.getExpression(attribute, root); 
     this.predicates.add(criteriaBuilder.not(expression.in(values))); 
     return this; 
    } 

    public SimpleSelectBuilder<E> andIn(final Attribute attribute, final Collection<Object> values) { 
     final Expression expression = this.getExpression(attribute, root); 
     this.predicates.add(expression.in(values)); 
     return this; 
    } 


    public SimpleSelectBuilder<E> andContains(final Attribute attribute, final Object value) { 

     final Expression expression = this.getExpression(attribute, root); 
     this.predicates.add(criteriaBuilder.isMember(value, expression)); 
     return this; 
    } 

    public SimpleSelectBuilder<E> orderByAsc(final Attribute attribute) { 
     final List<Order> orders = new ArrayList<>(); 
     if (this.criteriaQuery.getOrderList() != null) { 
      orders.addAll(this.criteriaQuery.getOrderList()); 
     } 
     orders.add(criteriaBuilder.asc(this.getExpression(attribute, root))); 
     this.criteriaQuery.orderBy(orders.toArray(new Order[orders.size()])); 
     return this; 
    } 

    public SimpleSelectBuilder<E> orderByDesc(final Attribute attribute) { 
     List<Order> orders = this.criteriaQuery.getOrderList(); 
     if (orders == null) { 
      orders = new ArrayList<>(); 
     } 
     orders.add(criteriaBuilder.desc(this.getExpression(attribute, root))); 
     this.criteriaQuery.orderBy(orders.toArray(new Order[orders.size()])); 
     return this; 
    } 

    public SimpleSelectBuilder<E> setFirst(Integer first) { 
     this.first = first; 
     return this; 
    } 

    public SimpleSelectBuilder<E> setMax(Integer max) { 
     this.max = max; 
     return this; 
    } 

    public SimpleSelectBuilder<E> setLockModeType(LockModeType lockModeType) { 
     this.lockModeType = lockModeType; 
     return this; 
    } 

    public List<E> getResultList() { 
     final TypedQuery<E> query = this.prepareQuery(); 

     if (lockModeType != null) { 
      query.setLockMode(lockModeType); 
     } 

     if (first != null) { 
      query.setFirstResult(first); 
     } 

     if (max != null) { 
      query.setMaxResults(max); 
     } 

     return query.getResultList(); 
    } 

    public List<E> getCacheableResultList() { 
     final TypedQuery<E> query = this.prepareQuery(); 

     if (lockModeType != null) { 
      query.setLockMode(lockModeType); 
     } 

     if (first != null) { 
      query.setFirstResult(first); 
     } 

     if (max != null) { 
      query.setMaxResults(max); 
     } 

     query.setHint("org.hibernate.cacheable", true); 
     query.setHint("org.hibernate.cacheMode", "NORMAL"); 
     return query.getResultList(); 
    } 

    public E getSingleResult() { 
     final TypedQuery<E> query = this.prepareQuery(); 

     if (lockModeType != null) { 
      query.setLockMode(lockModeType); 
     } 

     return query.getSingleResult(); 
    } 

    public E getCacheableSingleResult() { 
     final TypedQuery<E> query = this.prepareQuery(); 

     if (lockModeType != null) { 
      query.setLockMode(lockModeType); 
     } 

     query.setHint("org.hibernate.cacheable", true); 
     query.setHint("org.hibernate.cacheMode", "NORMAL"); 
     return query.getSingleResult(); 
    } 

    private TypedQuery<E> prepareQuery() { 
     this.criteriaQuery.where(this.predicates.toArray(new Predicate[this.predicates.size()])); 
     return this.entityManager.createQuery(criteriaQuery); 
    } 

    private <T> Expression<T> getExpression(final Attribute attribute, final From<E, T> from) { 
     if (attribute instanceof SingularAttribute) { 
      SingularAttribute singularAttribute = (SingularAttribute) attribute; 
      return from.get(singularAttribute); 
     } else if (attribute instanceof PluralAttribute) { 
      PluralAttribute pluralAttribute = (PluralAttribute) attribute; 
      return from.get(pluralAttribute); 
     } else { 
      throw new PersistenceException("Attribute type of '" + attribute 
        + "' must be one of [SingularAttribute, PluralAttribute]."); 
     } 
    } 

    private <T> Join<E, T> getJoinExpression(final Attribute attribute, final From<E, T> from) { 
     if (attribute instanceof SingularAttribute) { 
      final SingularAttribute singularAttribute = (SingularAttribute) attribute; 
      return from.join(singularAttribute); 
     } else if (attribute instanceof CollectionAttribute) { 
      final CollectionAttribute collectionAttribute = (CollectionAttribute) attribute; 
      return from.join(collectionAttribute); 
     } else { 
      throw new PersistenceException("Attribute type of '" + attribute 
        + "' must be one of [SingularAttribute, PluralAttribute]."); 
     } 
    } 

    public SimpleSelectBuilder<E> joinAnd(final Attribute attribute, final Object value, final Attribute... joinOn) { 
     Join tableJoin = null; 
     for (final Attribute join : joinOn) { 
      if (tableJoin == null) { 
       tableJoin = this.getJoinExpression(join, root); 
      } else { 
       tableJoin = this.getJoinExpression(join, tableJoin); 
      } 

     } 

     if (tableJoin == null) { 
      throw new PersistenceException("SelectBuilder cannot construct your join statement"); 
     } 

     final Expression expression = this.getExpression(attribute, tableJoin); 
     this.predicates.add(criteriaBuilder.equal(expression, value)); 
     return this; 
    } 
} 

Если вы используете это. Чем ваш метод станет таким.

public List <Product> getProducts(String prodId, String prodName) { 

    // TODO add like statement to SimpleSelectBuilder 
    return new SimpleSelectBuilder<Product>(this.getEntityManager(), Product.class) 
      .and(Product_.prodId, prodId)) 
      .and(Product_.prodName, prodName)) 
      .getResultList(); 

} 

Это будет лучше, если вы написать свой собственный SimpleSelectBuilder для обработки стереотипных блоков коды и увеличить повторное использование. Например, вам нужно добавить оператор like в код выше.

Управление всеми слоями, транзакциями, пулами соединений и т. Д. Займет много времени. Вместо этого вы можете захотеть рассмотреть промежуточное программное обеспечение, чтобы управлять всем этим для вашего. В моих проектах я предпочитаю Весну.

+0

Прежде всего за подробное объяснение. Я использую EJB, а не Spring. Да, я разбиваюсь на разные слои. Я проанализирую ваше решение. Оценил. – user75ponic

+0

@bhdrkn Это потрясающее решение!Я скопировал ваш SimpleSelectBuilder и добавил около 6-8 других методов (например, «like», «include» (aka join), «or», «subEntityPropertyEquals» (чтобы я мог фильтровать родителя по одному из свойств его дочернего элемента и группу из других) .Я перехожу от C# к Java и, используя этот свободный класс строителей, я создал что-то похожее на LINQ, так что, надеюсь, это поможет мне и моей команде сделать переход! Еще раз спасибо. – HenryC