【问题标题】:Spring Data JPA: how to return empty result when repository method parameter of Collection type is empty?Spring Data JPA:当Collection类型的repository方法参数为空时如何返回空结果?
【发布时间】:2018-01-03 06:03:11
【问题描述】:

我正在使用 Spring Data JPA v1.10.2 还有一个用例:

ClientDao.java:

List<Client> getClientsByUnitsIn(@NonNull Collection<Unit> units);

这个方法生成一个类似这样的 SQL 查询:

SELECT * FROM clients WHERE units in (?1)

当我为存储库方法添加@Query注解时,也有类似的情况:

@Query("SELECT c FROM Client c WHERE c.unit IN (?1)")
List<Client> getSpecificClients(@NonNull Collection<Unit> units)

但在许多情况下,参数units 可能为空。在这种情况下,该方法应该返回空结果,但它只是失败并显示有关错误 SQL 语句的消息。

我使用一种解决方法:向存储库添加一个默认方法,如下所示:

default List<Client> getSpecificClientsOrEmpty(@NonNull Collection<Unit> units){
    if (units.isEmpty) {
        return emptyList();
    }
    return getSpecificClients(units);
}

但我不喜欢这种解决方法:

  • 我必须为每个案例创建一个额外的方法
  • 我必须检查是否只有默认方法在代码中使用,因为没有编译时检查,如果我错过了一些使用,我会得到一个运行时异常。

有人有更好的解决方案吗?

【问题讨论】:

标签: java spring jpa spring-data


【解决方案1】:

1) 使用 getSpecificClients() 存储库实现中的样板代码编写您自己的查询:

public List<Client> getSpecificClients(@NonNull Collection<Unit> units){
    if (units.isEmpty()) {
        return emptyList();
    }
    return em.createQuery("SELECT c FROM Client c WHERE c.unit IN (?1)", Unit.class)
             .setParameter(1, units)
             .getResultList();  
}

如果此预处理在您的存储库中是不常见的要求,则应采用这种方式。
这有点冗长,但在少数情况下仍然可以接受。

2) 使用 AOP 横向制作。
在你需要的每个方法之前定义一个Aspect 来做这个处理:

if (units.isEmpty) {
    return emptyList();
}

请注意,只有在预处理要求足够频繁时才应使用这种方式,因为它会增加应用程序的复杂性和一般设置。

3) 您可以在接受Function 作为参数的基本接口存储库中创建一个通用默认方法,以便能够将任何要执行的方法传递给该方法:

@SuppressWarnings("unchecked")
default<T, U> List<U> selectWithIn(Collection<T> valueForInClause, Function<Collection<T>, List<U>> function) {
    if (valueForInClause.isEmpty()) {
        return new ArrayList<U>();
    }
    return function.apply(valueForInClause);
}

ClientDAO 课程中,您仍然会有这个:

@Query("SELECT c FROM Client c WHERE c.unit IN (?1)")
List<Client> getSpecificClients(@NonNull Collection<Unit> units)

在 DAO 的客户端代码中,您可以通过这种方式调用 selectWithIn() 方法:

private ClientDAO clientDAO;
...
List<Unit> units = ...;
List<Client> clients = clientDAO.selectWithIn(units, (o) -> clientDAO.getSpecificClients(o));

它并不太冗长,它节省了一些代码行,但我真的不喜欢这种方式,因为它会使 DAO 客户端类的单元测试更加复杂。

【讨论】:

  • 自己的实现看起来很丑。顺便说一句,这需要添加一个虚拟存储库实现类。至于方面 - 谢谢,我会考虑的。
  • 有一个有趣的点:当使用 Spring Data 查询方法时,它是开箱即用的。但是当使用 Query 注释时 - 它没有。为什么?是否可以在 Spring 中进行配置?
  • 使用提供流畅 API 的 JPA 2,我发现它并没有那么难看。 “使用 Spring Data 查询方法时”是什么意思?即使使用这些方法,您也应该专门处理您的优化:if (units.isEmpty) { return emptyList(); }
  • 当然,来自 JPA 2 的 Criteria API 看起来不错,但是来吧,在 every 存储库方法中编写 if (...) {return emptyList() - 这不是一个好主意 :) AOP 的发明是为了避免这样的东西。如果我找不到更好的方法,我会做 AOP。
  • 我同意你的看法。 AOP 确实是更好的选择。但我只会在需求有点频繁的情况下才介绍它。将 AOP 工具设置为拦截 3 或 4 个方法并不是一个好交易。您增加了应用程序的复杂性和一般设置以节省一些代码行。
猜你喜欢
  • 1970-01-01
  • 2017-05-01
  • 1970-01-01
  • 2022-12-19
  • 2012-08-31
  • 2020-11-06
  • 1970-01-01
  • 2021-01-12
  • 1970-01-01
相关资源
最近更新 更多