简体   繁体   English

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

[英]Best practice for JPA with Java8's Optional return?

I love Java8's semantics. 我喜欢Java8的语义。 I use a lot of such code in my DAOs : 我在我的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. 它运行良好,但这样的代码(尝试捕获NoResultException)散布在我的DAO上。 And I have to catch Exception , which somehow lowers performance . 我必须捕捉异常,这会以某种方式降低性能。

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 ? 如果不可能(因为在JPA中定义了NoResultException),任何“模板化”这样的工作流程的快捷方式?

Thanks. 谢谢。

If course you can templatize it, using the magic of lambdas! 如果当然你可以使用lambdas的魔力来模板化它!

Start with an @FunctionalInterface to define the lambda's contract: @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 static in your calling code, your method above becomes: 现在,在您的调用代码中使用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());
}

So here, () -> emp.get()... is a lambda that captures the retrieval behaviour. 所以这里, () -> emp.get()...是一个捕获检索行为的lambda。 The interface DaoRetriever is allowed to throw a NoResultException so the lambda is too. 允许interface DaoRetriever抛出NoResultException因此lambda也是如此。

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. 这具有更简单的优点,但是如果有的话,简单地丢弃其他结果的缺点。

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. 我们需要一个不同arities的族或函数接口,它们将抛出异常的lambda转换为不抛出异常的lambda。 FunctionalJava ( http://www.functionaljava.org/ ) does this: 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/ ). 请注意,验证在失败案例中是例外,如果成功则是常规值( 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): 这个方法看起来像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();
        }
    };
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM