Каков хороший дизайн для "слоя" запроса для Java JPA?

В JPA Entities - это хорошие аннотированные простые старые Java-объекты. Но я не нашел хорошего способа взаимодействовать с ними и с базой данных.

В моем текущем приложении мой базовый дизайн всегда заключается в том, чтобы в качестве первичного ключа всегда был идентификатор на основе последовательности, поэтому мне обычно приходится искать объекты по другим свойствам, кроме PK.

И для каждой Entity у меня есть EJB без сохранения состояния

@Stateless
public class MyEntApiBean implements MyEntApi {


@PersistenceContext(unitName = "xxx") @Inject EntityManager entityManager;

с методами запроса, которые все являются вариациями

/**
 * @return A List of all MyEnts that have some property
 * @param someProp some property
 */
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {

    try {
        final Query query = entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp");
        query.setParameter("someProp", someProp);
        return query.getResultList();
    } catch(final NoResultException nre) {
        log.warn("No MyEnts found");
    }
    return new ArrayList<MyEnt>();
}

Так:

  1. Я действительно ненавижу наличие этих методов в EJB, потому что они кажутся принадлежащими самим объектам, а локальные интерфейсы EJB меня до чертиков раздражают.

  2. Я ненавижу дублирование, которое у меня есть в каждом методе с «try, createQuery, getResultList, catch, log, return» (в основном из-за отсутствия замыканий или «with statement» или чего-то подобного в Java).

Есть ли у кого-нибудь предложение лучшего способа взаимодействия с объектами и базой данных, который решает одну или обе мои проблемы?

В настоящее время я подумываю о некоторых базовых методах с обобщениями и отражением, чтобы получить некоторые общие методы запросов, чтобы уменьшить дублирование (проблема 2) (позже я поставлю прототип для обзора).

Спасибо, Андерс

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
0
1 504
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Попробуйте Шов. Объекты запроса выполняет большую часть работы за вас, и их легко расширить. Или вы всегда можете реализовать аналогичный шаблон.

В общем, Seam делает много полезных вещей, чтобы преодолеть разрыв между JPA, вашим просмотром и бизнес-уровнями. Чтобы Seam был полезным, необязательно использовать JSF.

Если вы выполняете много текстовых поисков, возможно, вам также стоит рассмотреть некоторую структуру индексации, такую ​​как Компас.
Я не знаю, подходит ли это вашему приложению, но если да, то он может улучшить дизайн кода и производительность.

На самом деле я использую Шов. И предложение объекта Query привело меня к поиску Hibernates Критерии запросов (Query By Example) функциональность. Кажется, это очень похоже на то, что я искал.

Может, в базовом классе и с черта дженериков ....?

Вы излишне многословны. Во-первых, getResultList () не генерирует исключение, когда строки не возвращаются (по крайней мере, не в Eclipse или Toplink - я не могу представить, чтобы другой провайдер был другим). getSingleResult () - нет, getResultList () - нет. Кроме того, вы можете использовать шаблон строителя так:

@SuppressWarnings("unchecked")
public List<MyEnt> getAllMyEntsFromProp(final String someProp) {
  return entityManager.createQuery("select me from MyEnt me where me.someProp = :someProp")
    .setParameter("someProp", someProp);
    .getResultList();
}

должно быть достаточно для возврата списка результатов, если он есть, или пустого списка, если его нет. Обратите внимание на две вещи:

  1. @SuppressWarnings ("unchecked") не требуется, но он избавляет от неизбежного в противном случае предупреждения при преобразовании неуниверсального результата List из getResultList () в общий List; и

  2. Вероятно, стоит заменить вызов createQuery () на @NamedQuery на MyEnt (обычно). Во-первых, это позволит выполнять проверку во время развертывания и другие полезные вещи.

Его достаточно кратко и полно.

Мойн!

Вот моя версия для отдельных результатов (я использую ее в своих настольных JPA-приложениях с основами TopLink):

public class JPA {
  @SuppressWarnings ("unchecked")
  public static <T> T querySingle(
      EntityManager em, 
      Class<T> clazz, 
      String namedQuery, 
      Pair... params) 
  {
    Query q = em.createNamedQuery(namedQuery);
    for (Pair pair : params) {
      q.setParameter(pair.key, pair.value);      
    }
    List<T> result = q.getResultList();
    if ( result.size() == 0 ) {
      return null;
    }
    if ( result.size() == 1 ) {
      return result.get(0);
    }
    throw new 
      IllegalStateException(
        "To many result rows for query: " 
         + namedQuery 
         + " where " 
         + Arrays.toString(params));
  }

  public static class Pair {
    String key;
    Object value;

    public static Pair param (String key, Object value) {
      return new Pair (key, value);
    }

    public Pair (String key, Object value) {
      this.key = key;
      this.value = value;
    }

    @Override
    public String toString() {
      return key + " = " + value;
    }
  }
}

И использование:

import static org.sepix.JPA.*;
...

String id = ...
Customer customer = querySingle (em, Customer.class, 
                      "Customer.findByID", Pair.param ("id", id));

или же:

String inquiryID = ...
Boolean current = Boolean.TRUE;
Inquiry inq = querySingle (em, Inquiry.class, 
                      "Inquiry.findCurrent", 
                      Pair.param ("inquiry", inquiryID),
                      Pair.param ("current", current));

наилучшие пожелания, Джош.

Я предпочитаю использовать Spring JpaDaoSupport, который помогает справляться с JPA. Хороший пример - http://github.com/rafalrusin/jpaqb/blob/master/src/test/java/jpaqb/CarDao.java.

Хорошее разделение логики - наличие класса DAO (объект доступа к данным) и DTO (объект передачи данных). DAO обычно содержит все необходимые запросы, а DTO - это сущности с полями.

Другие вопросы по теме