Лучшая практика для 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" такого рабочего процесса?
Спасибо.
Ответы
Ответ 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();
}
};
}