Создайте универсальный метод, использующий JPA2 и CriteriaBuilder.

Я разрабатываю веб-приложение, использующее фреймворки JSF2 и JPA2. Я начал использовать мастера в netbeans7.0, и «Новые страницы JSF из классов сущностей», созданные classAbstractFacade, содержат этот полезный метод:

public List<T> findRange(int[] range) {
  javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
  cq.select(cq.from(entityClass));
  javax.persistence.Query q = getEntityManager().createQuery(cq);
  q.setMaxResults(range[1] - range[0]);
  q.setFirstResult(range[0]);
  return q.getResultList();
}

где T — это класс сущности, который содержит структуру базы данных таблицы, а переменная диапазона содержит два числа для получения подсписка результатов! Теперь я изменяю его, чтобы установить порядок по предложению и проверить, является ли пользователь диапазоном или нет, и новый код:

    public List<T> findRangeOrdered(int[] range, String orderBy) {
      javax.persistence.criteria.CriteriaQuery<T> cq = (javax.persistence.criteria.CriteriaQuery<T>)getEntityManager().getCriteriaBuilder().createQuery();
      cq.select(cq.from(entityClass));
      javax.persistence.criteria.Root<T> r = cq.from(entityClass);
      if(orderBy!=null && !orderBy.equals("")){
        if(orderBy.ebdsWith(" desc"))
          cq.orderBy(getEntityManager().getCriteriaBuilder().desc(r.get(orderBy.substring(0,orderBy.indexOf(" ")))));
        else
          cq.orderBy(getEntityManager().getCriteriaBuilder().desc(r.get(orderBy.substring(0,orderBy.indexOf(" ")))));
      }
      javax.persistence.Query q = getEntityManager().createQuery(cq);
      if(range!=null)
      {
        q.setMaxResults(range[1] - range[0]);
        q.setFirstResult(range[0]);
      }
      return q.getResultList();
    }

Теперь я бы добавил несколько вариантов. Я думаю о добавлении переменной List, которая может содержать HashMap с этой парой значений key-->:

  • "тип" --> тип поля, которое я бы искал (например, String.class или java.util.Date.class);
  • "имя" --> имя поля в классе сущностей;
  • "value0" --> первое значение, которое я ищу;
  • "value1" --> второе значение для предложения "between";
  • "предложение" --> какой фильтр я хочу использовать.

Теперь у меня есть некоторые сомнения:

1) это хорошая идея?

2) сделать выход из фреймворка, который уже реализует эту функцию?

3) если ответы на предыдущие вопросы: Да, это хорошая идея; и Нет, нет никакой структуры, которая уже делает это, я нахожу эти новые проблемы: этот новый метод возвращает мне некоторые ошибки компилятора:

public List<T> findAllFiltered(int[] range, String orderBy, List<HashMap<String,Object>> whereList) {
  List<Predicate> predicateList = new ArrayList<Predicate>();
  String buf;
  HashMap<String,Object> where;
  int i=0;
  javax.persistence.criteria.CriteriaQuery<T> cq = (javax.persistence.criteria.CriteriaQuery<T>)getEntityManager().getCriteriaBuilder().createQuery();
  cq.select(cq.from(entityClass));
  javax.persistence.criteria.Root<T> r = cq.from(entityClass);
  javax.persistence.criteria.Path<T> path = cq.from(entityClass);
  if(whereList!= null && whereList.size()>0){
    Iterator iter=whereList.iterator();
    Predicate p;
    while (iter.hasNext())    
    {
      where=(HashMap<String,Object>)iter.next();

      if(where.get("where").toString().equals("like"))
        p = getEntityManager().getCriteriaBuilder().like(getEntityManager().getCriteriaBuilder().upper(r.<String>get(where.get("name").toString())), "%"+where.get("value0").toString().toUpperCase()+"%");
      else if(where.get("where").toString().equals("equal"))
        p = getEntityManager().getCriteriaBuilder().equal(r.get(where.get("name").toString()), where.get("value0").toString());
      else if(where.get("where").toString().equals("in"))
        p = getEntityManager().getCriteriaBuilder().and(r.get(where.get("name").toString()).in(where.get("value0").toString().replaceAll("#",",")));
      else if(where.get("where").toString().equals("between"))
      {
        java.util.Date value0=(java.util.Date)where.get("value0");
        java.util.Date value1=(java.util.Date)where.get("value1");
        p = getEntityManager().getCriteriaBuilder().between(r.<java.util.Date>get(where.get("name").toString()),value0,value1);
      }
      else if(where.get("where").toString().equals("greatest"))
        p = getEntityManager().getCriteriaBuilder().and(getEntityManager().getCriteriaBuilder().greatest(r.<Long>get(where.get("name").toString())));
      else if(where.get("where").toString().equals("greaterThanOrEqualTo"))
      {
        java.util.Date value0=(java.util.Date)where.get("value0");
        p = getEntityManager().getCriteriaBuilder().greaterThanOrEqualTo(r.<java.util.Date>get(where.get("name").toString()),value0);
      }
      else if(where.get("where").toString().equals("lessThanOrEqualTo"))
      {
        java.util.Date value0=(java.util.Date)where.get("value0");
        p = getEntityManager().getCriteriaBuilder().lessThanOrEqualTo(r.<java.util.Date>get(where.get("name").toString()),value0);
      }

    }
  }
  Predicate[] predicates = new Predicate[predicateList.size()];
  predicateList.toArray(predicates);
  cq.where(predicates);

  if(orderBy!= null&& !orderBy.equals("")){
    if(orderBy.endsWith("desc"))
      cq.orderBy(getEntityManager().getCriteriaBuilder().desc(r.get(orderBy.substring(0,orderBy.indexOf(" ")))));
    else
      cq.orderBy(getEntityManager().getCriteriaBuilder().asc(r.get(orderBy.substring(0,orderBy.indexOf(" ")))));
  }

  javax.persistence.Query q = getEntityManager().createQuery(cq);
  if(range!=null){
    q.setMaxResults(range[1] - range[0]);
    q.setFirstResult(range[0]);
  }
  return q.getResultList();
}

1) «Величайший» метод возвращает эту ошибку:

p = getEntityManager().getCriteriaBuilder().and(getEntityManager().getCriteriaBuilder().greatest(r.<Long>get(where.get("name").toString())));
method CriteriaBuilder.and(Expression<Boolean>,Expression<Boolean>) is not applicable
(actual and formal argument lists differ in length)
method CriteriaBuilder.and(Predicate...) is not applicable
(argument type Expression<Long> does not conform to vararg element type Predicate)

и я ищу пример для этого метода, но не нашел ничего полезного;

2) как я уже писал, мой HashMapp также содержит класс поля, который я хочу отфильтровать, теперь я бы использовал эту информацию для перезаписи между, GreaterThanOrEqualTo, lessThanOrEqualTo, поэтому, если бы я хотел, я мог бы фильтровать некоторые другие типы, чем java.util. Дата! Что вы думаете об этом длинном вопросе? Спасибо за помощь!

P.S.: Извините за мой не идеальный английский, но я итальянец.

P.S.2: Извините также за структуру моего кода... Я слепой и не всегда контролирую отступы... этот сайт контролирует их, но я не уверен, что правильно делаю отступы во всем коде!


person Filippo1980    schedule 29.11.2011    source источник


Ответы (2)


ИМХО, это не лучшая идея. Вы заново изобретаете новый общий язык запросов, и этот новый язык запросов будет

  • менее выразительный, чем JPQL
  • нестандартный
  • менее типобезопасен, чем Criteria API
  • менее эффективен, чем JPQL и Criteria

Итак, ответы на ваши вопросы

  1. Нет, это не очень хорошая идея.
  2. Да, такая структура уже существует. Он называется JPQL.
person JB Nizet    schedule 29.11.2011
comment
@ jb-nizet: значит, вы предлагаете передать строку, содержащую запрос, который я хочу выполнить? - person Filippo1980; 29.11.2011
comment
Нет. Я предлагаю определить специальный метод бизнес-службы с конкретными аргументами, четким контрактом и javadoc и выполнить соответствующий конкретный запрос HQL или Criteria из этой службы. Уровень представления не должен иметь дело с запросами. Он должен вызывать определенные службы с четко определенным контрактом и интерфейсом, и эти службы должны обрабатывать запросы. - person JB Nizet; 29.11.2011
comment
Извините, jb-nizet, но я не понимаю, что вы имеете в виду ... Вы говорите о чем-то вроде: класс table1Query содержит больше методов для запроса полей, содержащихся в таблице 1, класс table2Query содержит все методы для фильтрации table2 ecc? Если это не так, можете ли вы написать простой практический пример или дать ссылку на один пример? Спасибо за помощь! - person Filippo1980; 29.11.2011
comment
Контроллеры JSF не должны выполнять запросы напрямую. Им следует позвонить в бизнес-службу. Например, страница поиска клиентов может использовать службу CustomerManager, и эта служба будет иметь такие методы, как findCustomersByFirstNameAndLastName(String firstName, String lastName), createCustomer(Customer customer). Эти методы будут транзакционными, будут использовать API-интерфейс JPA и выполнять запросы, но также будут содержать бизнес-логику, например, когда создается клиент, письмо должно быть отправлено какому-либо администратору. Нет необходимости в каком-либо универсальном методе searchAnything(SearchCriteria). - person JB Nizet; 29.11.2011
comment
Хорошо, я сделаю такой метод! В очередной раз благодарим за помощь! P.S.: Когда у меня будет 15 очков репутации, я проголосую за вас... - person Filippo1980; 29.11.2011
comment
Вам, вероятно, следует прочитать шаблоны объектов доступа к данным и аналогичные шаблоны для доступа к данным. - person Thor84no; 29.11.2011

Я не думаю, что создание метода, который использует строку для построения запроса с помощью Criteria API, является хорошей идеей, я определенно предлагаю использовать JPQL или, если вы предпочитаете какой-либо другой инструмент, который поможет вам построить запрос самым разумным образом, вы можете использовать : ObjectQuery или Query Dsl. или Torpedo Query, если вы хотите изолировать свой код от фактической реализации, подумайте о том, чтобы скрыть запрос внутри некоторого DAO. или объект репозитория, или хотя бы создайте оболочку над каким-нибудь построителем запросов!

person tglman    schedule 04.10.2013