JPA 的最佳实践与 Java8 的可选返回?

2022-09-02 20:40:52

我喜欢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上。而且我必须抓住 异常 ,这以某种方式降低了性能 。

我想知道它是否是最好的解决方案?或任何更好的解决方案,没有尝试捕获?

如果这是不可能的(因为NoResultException是在JPA中定义的),那么“模板化”这种工作流程的任何快捷方式?

谢谢。


答案 1

当然,你可以模板化它,使用lambdas的魔力!

从 定义 lambda 的合约开始:@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();
}

现在,在调用代码中使用 a,上面的方法将变为: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());
}

所以这里,是一个捕获检索行为的lambda。允许抛出一个,所以 lambda 也是。() -> emp.get()...interface DaoRetrieverNoResultException

或者,我将使用 - - 的另一种方法,并按如下方式更改代码:TypedQuerygetResultList

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

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

我们需要一个具有不同 arities 的系列或功能接口,这些接口将引发异常的 lambda 转换为不引发异常的 lambda。FunctionalJava (http://www.functionaljava.org/) 是这样做的:

所以我们有一个尝试类家族: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/)。如果不关心异常,则可以将失败事例转换为空可选,并将成功事例转换为可选值。这种方法看起来像鲍里斯的方法,但没有道引用(这是无关紧要的):

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

推荐