java 带有 Java8 的 Optional return 的 JPA 最佳实践?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28249874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:16:19  来源:igfitidea点击:

Best practice for JPA with Java8's Optional return?

javajpajava-8

提问by smallufo

I love Java8's semantics. I use a lot of such code in my DAOs :

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

It works well , but such code (try catch NoResultException ) scatters over my DAOs. And I have to catch Exception , which somehow lowers performance .

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

I wonder if it the best solution ? or any better solution , without try-catch ?

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

If it is not possible (because NoResultException is defined in JPA) , any shortcut to 'templatize' such workflow ?

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

Thanks.

谢谢。

回答by Boris the Spider

If course you can templatize it, using the magic of lambdas!

当然,如果您可以使用 lambda 的魔力对其进行模板化!

Start with an @FunctionalInterfaceto define the lambda's contract:

从 an 开始@FunctionalInterface定义 lambda 的契约:

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

This is a Single Method Interface (or SMI) that will encapsulate the behaviour of your method.

这是一个单一方法接口(或 SMI),它将封装您的方法的行为。

Now create a utility method to use the 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();
}

Now, using an import staticin your calling code, your method above becomes:

现在,import static在您的调用代码中使用 an ,您上面的方法变为:

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

So here, () -> emp.get()...is a lambda that captures the retrieval behaviour. The interface DaoRetrieveris allowed to throw a NoResultExceptionso the lambda is too.

所以这里() -> emp.get()...是一个捕获检索行为的 lambda。在interface DaoRetriever被允许抛出NoResultException这样的拉姆达过。

Alternatively, I would use the other method of TypedQuery- getResultList- and change the code as follows:

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

This has the advantage of being simpler, but the disadvantage of simply discarding other results if there are any.

这具有更简单的优点,但缺点是简单地丢弃其他结果(如果有的话)。

回答by Mark Perry

Boris is on the right track, but it can be done better. We need some more abstraction. This conversion has nothing to do with daos.

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

We need a family or functional interfaces of different arities that convert lambdas that throw exceptions to those that don't. FunctionalJava (http://www.functionaljava.org/) does this:

我们需要一系列不同参数的函数式接口,将抛出异常的 lambda 转换为不抛出异常的 lambda。FunctionalJava ( http://www.functionaljava.org/) 这样做:

So we have a family of Try classes: Try0, Try1, etc.

所以我们有一系列 Try 类:Try0、Try1 等。

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

and we want to convert that to a function that does not throw an exception:

我们想把它转换成一个不抛出异常的函数:

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

Note that Validation is either an exception in the failure case or the regular value if it succeeds (https://functionaljava.ci.cloudbees.com/job/master/javadoc/). If you don't care about the exception you can transform the failure case to the empty optional and the success case to have the value in the optional. This method looks like Boris's, but without the dao references (which are irrelevant):

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