使用 Java8 的可选 return 的 JPA 最佳实践?

Best practice for JPA with Java8's Optional return?

我喜欢 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();
    }
  }

它运行良好,但这样的代码(尝试捕获 NoResultException)分散在我的 DAO 中。而且我必须捕获 Exception ,这会以某种方式降低性能。

我想知道这是否是最佳解决方案?或者没有 try-catch 的更好的解决方案?

如果不可能(因为 NoResultException 是在 JPA 中定义的),'templatize' 此类工作流的任何快捷方式?

谢谢。

如果您当然可以使用 lambda 的魔力将其模板化!

@FunctionalInterface 开始定义 lambda 的契约:

@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()... 是一个捕获检索行为的 lambda。 interface DaoRetriever 被允许抛出一个 NoResultException 所以 lambda 也是如此。

或者,我会使用 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();
}

这样做的优点是更简单,但缺点是如果有其他结果就简单地丢弃。

鲍里斯走在正确的轨道上,但可以做得更好。我们需要更多的抽象。此转换与daos无关。

我们需要一个系列或不同参数的函数式接口来将抛出异常的 lambda 转换为不抛出异常的 lambda。 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);
        }
    };
}

请注意,Validation 要么是失败情况下的异常,要么是成功时的常规值 (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();
        }
    };
}