Spring Data查询方法与可选@Param

19

在Spring Data REST中,是否可以允许查询方法@Params是可选的?

例如,我想将非常相似的搜索绑定到同一资源路径。 目前要实现这个目标,需要像以下这样:

@RestResource(path = "driver", rel = "byDriver")
List<Bar> findByDriverId(@Param("id") String id, Pageable pageable);

@RestResource(path = "driverAndSpan", rel = "byDriverAndSpan")
List<Bar> findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(@Param("id") String id, @Param("start") Date start,
        @Param("end") Date end, Pageable pageable);

这使我得到:

byDriver: {
  href: "http://localhost:8080/foo/search/driver{?id,page,size,sort}",
},
byDriverAndSpan: {
  href: "http://localhost:8080/foo/search/driverAndSpan{?id,start,end,page,size,sort}",
}

我想要的是能够看到类似以下路径的内容,其中startend是可选参数,而不是在我的Repository中定义多个方法。

byDriverAndSpan: {
  href: "http://localhost:8080/foo/search/driverAndSpan{?id,*start,*end,page,size,sort}",
}

可能会像这样:

@RestResource(path = "driverAndSpan", rel = "byDriverAndSpan")
List<Bar> findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(@Param("id") String id, @Param(value = "start", optional = true) Date start,
        @Param(value = "end", optional = true) Date end, Pageable pageable);

挖掘一篇旧帖...我很惊讶没有人提到Querydsl,这正是你可以使用的工具。 - Marc Tarin
5个回答

1
也许你可以定义一个名为"default"的"main"函数,它将委托给其他函数。就像这样:
@RestResource(path = "driverAndSpan", rel = "byDriverAndSpan")
default List<Bar> findByDriverIdAndOptionalStartTimeGreaterThanEqualAndOptionalEndTimeLessThanEqual(@Param("id") String id, @Param(value = "start", optional = true) Date start,
        @Param(value = "end", optional = true) Date end, Pageable pageable) {
   if(start != null && end != null) {
      return findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(id, start, end, pageable);
   }
   return findByDriverId(id, pageable);
}

我认为你甚至可以将Optional作为参数类型使用,然后你就可以使用函数重载了。

@RestResource(path = "driverAndSpan", rel = "byDriverAndSpan")
default List<Bar> findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(
@Param("id") String id, @Param(value = "start") Optional<Date> start,
        @Param(value = "end") Optional<Date> end, Pageable pageable) {
   if (!start.isEmpty() && !end.isEmpty()) {
      return findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(id, start.get(), end.get(), pageable);
   }
   return findByDriverId(id, pageable);
}

List<Bar> findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(String id, Date start, Date end, Pageable pageable);

注意,在这种情况下,您不应该暴露其他端点,只需暴露此默认方法。

0

不,目前不支持这个功能。如果支持的话会存在歧义的风险。有可能出现传入URL匹配多个@RestResource的情况。

从Java的角度来解释,假设我们可以定义两个方法:

getPerson(String firstName, int age);

getPerson(String firstName, {Optional} int age, int phoneNumber);

当有人试图调用第二个方法时,不提供年龄但被映射到第一个方法并将电话号码读取为年龄时,可能会出现问题。


为什么会存在歧义的风险?参数都有(唯一)名称。 - Nefreo
你在请求中提供了参数名称吗? - UserF40
3
楼主希望得到类似于 http://localhost:8080/foo/search/driverAndSpan{?id,*start,*end,page,size,sort} 的东西。在这种情况下,参数是通过名称提供的,因此不应该存在歧义。 - Nefreo

0
正如UserF40所说,这是不被支持的。我们曾经遇到过类似的问题,并通过根据提供的可选参数动态构建查询来解决它。
您可以使用Criteria API来实现这一点,或者通过检查哪些参数存在来在另一个服务类中动态构建SQL。

还可以在Spring Data中使用规格。 - Mahdiyar Zerehpoush

0

使用 org.springframework.data.jpa.repository.JpaSpecificationExecutor;

步骤1:在您的JPA存储库中实现JpaSpecificationExecutor

例如:

public interface TicketRepo extends JpaRepository<Ticket, Long>, JpaSpecificationExecutor<Ticket> {

步骤2 现在,您可以使用CriteriaBuilder构建规范查询来基于可选参数获取门票。

例如:

public Specification<Ticket> getTicketQuery(Integer domainId, Calendar startDate, Calendar endDate, Integer gameId, Integer drawId) {
    return (root, query, criteriaBuilder) -> {
        List<Predicate> predicates = new ArrayList<>();

        predicates.add(criteriaBuilder.equal(root.get("domainId"), domainId));
        predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createdAt"), startDate));
        predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("createdAt"), endDate));

        if (gameId != null) {
            predicates.add(criteriaBuilder.equal(root.get("gameId"), gameId));
        }

        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    };
}

第三步:将Specification实例传递给jpaRepo.findAll(specification),它会返回您的实体对象列表(在此运行示例中为Tickets)。
ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll

-1

@Param中有一个"required"参数选项,您可以使用它。

@RestResource(path = "driverAndSpan", rel = "byDriverAndSpan")

List findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(@Param("id") String id, @Param(value = "start", required = false) Date start, @Param(value = "end", required = false) Date end, Pageable pageable);


2
@Param注解中没有required选项,只有@RequestParamrequired选项。 - CDT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接