Подтвердить что ты не робот

Лучшая практика для JPA с Java8 Дополнительный возврат?

Мне нравится семантика Java8. Я использую много таких кодов в своих DAO:

  public Optional<User> findBy(String username) {
    try {
      return Optional.of(
        emp.get().createQuery("select u from User u where u.username = :username" , User.class)
        .setParameter("username" , username)
        .setMaxResults(1)
        .getSingleResult()
      );
    } catch (NoResultException e) {
      return Optional.empty();
    }
  }

Он работает хорошо, но такой код (try catch NoResultException) рассеивается по моим DAO. И я должен поймать Exception, что как-то снижает производительность.

Интересно, это лучшее решение? или какое-либо лучшее решение, без пробной попытки?

Если это невозможно (поскольку NoResultException определено в JPA), любой ярлык для "templatize" такого рабочего процесса?

Спасибо.

4b9b3361

Ответ 1

Если вы можете запланировать курс, используя магию лямбда!

Начните с @FunctionalInterface для определения лямбда-контракта:

@FunctionalInterface
public interface DaoRetriever<T> {
    T retrieve() throws NoResultException;
}

Это интерфейс с одним методом (или SMI), который будет инкапсулировать поведение вашего метода.

Теперь создайте метод утилиты для использования SMI:

public static <T> Optional<T> findOrEmpty(final DaoRetriever<T> retriever) {
    try {
        return Optional.of(retriever.retrieve());
    } catch (NoResultException ex) {
        //log
    }
    return Optional.empty();
}

Теперь, используя import static в вашем вызывающем коде, ваш метод выше:

public Optional<User> findBy(String username) {
    return findOrEmpty(() ->
            emp.get().createQuery("select u from User u where u.username = :username", User.class)
                    .setParameter("username", username)
                    .setMaxResults(1)
                    .getSingleResult());
}

Итак, () -> emp.get()... - это лямбда, которая захватывает поведение поиска. interface DaoRetriever разрешено бросать NoResultException, поэтому лямбда тоже.

В качестве альтернативы, я бы использовал другой метод TypedQuery - getResultList - и изменил код следующим образом:

public Optional<User> findBy(String username) {
    return emp.get().createQuery("select u from User u where u.username = :username", User.class)
            .setParameter("username", username)
            .setMaxResults(1)
            .getResultList()
            .stream()
            .findFirst();
}

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

Ответ 2

Борис на правильном пути, но это можно сделать лучше. Нам нужна еще абстракция. Это преобразование не имеет ничего общего с daos.

Нам нужны семейные или функциональные интерфейсы разных явлений, которые преобразуют лямбда, которые бросают исключения из тех, которые этого не делают. FunctionalJava (http://www.functionaljava.org/) делает следующее:

Итак, у нас есть семейство классов Try: Try0, Try1 и т.д.

public interface Try0<A, Z extends Exception> {
    A f() throws Z;
}

и мы хотим преобразовать его в функцию, которая не генерирует исключения:

static public <A, E extends Exception> Supplier<Validation<E, B>> toSupplierValidation(final Try0<A, E> t) {
    return () -> {
        try {
            return Validation.success(t.f());
        } catch (Exception e) {
            return Validation.fail((E) e);
        }
    };
}

Обратите внимание, что проверка является либо исключением в случае сбоя, либо регулярным значением, если оно выполнено успешно (https://functionaljava.ci.cloudbees.com/job/master/javadoc/). Если вы не заботитесь об исключении, вы можете преобразовать случай сбоя в пустую опцию и случай успеха, чтобы иметь значение в необязательном. Этот метод выглядит как Борис, но без ссылок dao (что не имеет значения):

static public <A, E extends Exception> Supplier<Optional<A>> toSupplierOptional(final Try0<A, E> t) {
    return () -> {
        try {
            return Optional.of(t.f());
        } catch (Exception e) {
            return Optional.empty();
        }
    };
}