使用Java8可选返回的JPA最佳实践?
我喜欢Java8的语义。我在DAO中使用了很多这样的代码:使用Java8可选返回的JPA最佳实践?,java,jpa,java-8,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"
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();
}
}
它工作得很好,但这样的代码try-catch-NoResultException分散在我的DAO上。我必须捕捉异常,它会以某种方式降低性能
我想知道这是不是最好的解决办法?或者有更好的解决方案,而不必尝试捕获
如果由于JPA中定义了NoResultException而无法实现,那么是否有“模板化”此类工作流的快捷方式
谢谢。当然,如果您可以使用lambdas的魔力将其模板化 从@FunctionInterface开始定义lambda的合同:
@FunctionalInterface
public interface DaoRetriever<T> {
T retrieve() throws NoResultException;
}
所以在这里,->emp.得到。。。是捕获检索行为的lambda。允许接口DAORRetriever抛出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();
}
这样做的优点是更简单,但缺点是如果有其他结果,就直接丢弃它们。Boris走上了正确的道路,但可以做得更好。我们需要更多的抽象。此转换与DAO无关 我们需要一个不同算术的族或函数接口,将抛出异常的lambda转换为不抛出异常的lambda。FunctionalJava做到了这一点: 所以我们有一系列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);
}
};
}
请注意,验证在失败的情况下是一个异常,如果成功,则是常规值。如果您不关心异常,那么可以将失败案例转换为空可选案例,将成功案例转换为可选案例中的值。此方法看起来像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();
}
};
}
最好的解决方案是修复引发异常的代码,我猜是getSingleResult。没有结果不是异常情况,因此引发异常是不合适的。@T.J.Crowder这是JPA规范的一部分,由JPA提供商实现-不确定这是一个选项。@BoristheSpider:Yikes。看在上帝的份上,这是怎么通过审查的@T.J.克劳德设计委员会。。。结局总是不好。Hibernate的等价项只返回null,没有结果。但是,对于具有多个实际结果的情况,卡片中仍然存在一个例外。JPA查询不返回null的原因是,查询可能选择某个列,并且该列的内容为null。这与没有这样的争吵是不同的。这里有一些讨论:哇,太棒了,无论是模板还是stream.findFirst解决方案。我学到了很多。谢谢。@smallufo我的荣幸。只是稍微更改了SMI—使其成为一个泛型类。setMaxResults1保证不会获取额外的结果—我将使用该习惯用法创建一个模板accept query,调用setMaxResults1.getResultList.stream.findFirst。顺便说一句,我不会调用findOrEmpty方法——只要给定可选的返回类型,find就足够了。}catch NoResultException ex{这不是一个好主意,除非它是一个例外情况,它将被重新引用而不是被记录。谢谢。你的解决方案太抽象了,我无法理解。也许我需要一些时间来消化。谢谢你的FunctionalJava解决方案。我以前没有接触过FJ。如果DaoRetriever接口代表任何需要的函数,它会更好确定无参数,返回结果或引发一般异常。这是Try0接口。如果我将Try1编辑为Try0以匹配前面的示例,可能会有所帮助。
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);
}
};
}
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();
}
};
}