使用Java 8的JPA的最佳实践可选返回?

时间:2015-01-31 08:55:29

标签: java jpa java-8

我喜欢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上。我必须捕捉异常,这会以某种方式降低性能。

我想知道它是否是最好的解决方案?或任何更好的解决方案,没有try-catch?

如果不可能(因为在JPA中定义了NoResultException),任何&#39;模板化&#39;的快捷方式。这样的工作流

感谢。

2 个答案:

答案 0 :(得分:26)

如果当然你可以使用lambdas的魔法来模板化它!

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

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

答案 1 :(得分:3)

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

我们需要一个不同arities的系列或功能接口,它们将抛出异常的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);
        }
    };
}

请注意,验证在失败案例中是例外,如果成功则是常规值(https://functionaljava.ci.cloudbees.com/job/master/javadoc/)。如果您不关心异常,则可以将失败案例转换为空可选项和成功案例,以使值在可选项中。这个方法看起来像Boris,但没有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();
        }
    };
}