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