【问题标题】:Spring Data Query Method with Optional @Param带有可选@Param 的 Spring 数据查询方法
【发布时间】:2014-11-04 14:41:59
【问题描述】:

是否可以允许查询方法@Params 是可选的,特别是在 Spring Data REST 的情况下?

例如,我想将非常相似的搜索绑定到相同的资源路径。现在要做到这一点,我需要以下内容:

@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 是可选参数,而不是在我的存储库中定义多个方法。

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,这正是你可以使用的。

标签: java spring spring-data spring-data-rest


【解决方案1】:

也许您可以将“主”函数定义为default,它将委托给其他函数。像这样的

@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);

请注意,在这种情况下,您不应公开其他端点,而仅公开此默认方法。

【讨论】:

    【解决方案2】:

    不,目前不支持此功能。如果是这样,就会有模棱两可的风险。可能存在传入 url 匹配超过 1 个@RestResource 的情况。

    为了从 Java 的角度进行解释,假设我们可以定义两种方法:

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

    当有人打算调用第二个方法时,会出现问题,不提供年龄,而是映射到第一个方法,将 phoneNumber 读取为年龄。

    【讨论】:

    • 怎么会有歧义的风险?参数具有(唯一的)名称。
    • 您是否在请求中提供参数名称?
    • OP 声明他想要类似http://localhost:8080/foo/search/driverAndSpan{?id,*start,*end,page,size,sort} 的东西。在这种情况下,参数是按名称提供的,因此不应有歧义。
    【解决方案3】:

    正如 UserF40 所说,这不受支持。我们遇到了类似的问题,并通过根据提供的可选参数动态构建查询来解决它。

    您可以使用 Criteria API 来实现此目的,或者通过检查存在哪些参数来在另一个服务类中动态构建 SQL。

    【讨论】:

    • 你也可以在spring数据中使用规范
    【解决方案4】:

    使用 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]));
        };
    }
    

    第 3 步:将 Specification 实例传递给 jpaRepo.findAll(specification),它将返回您的实体对象列表(在运行示例中为 Tickets)

    ticketRepo.findAll(specification); // Pass output of function in step 2 to findAll
    

    【讨论】:

      【解决方案5】:

      @Param 中有一个“required”参数选项可供您使用。

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

      List findByDriverIdAndStartTimeGreaterThanEqualAndEndTimeLessThanEqual(@Param("id") String id, @Param(value = "start", required = false) 开始日期, @Param(value = "end", required = false) 日期结束,可分页);

      【讨论】:

      • @Param 注解中没有选项required。只有@RequestParamrequired 选项。
      猜你喜欢
      • 2018-08-16
      • 1970-01-01
      • 2019-03-16
      • 2015-12-20
      • 2019-04-22
      • 1970-01-01
      • 2016-01-19
      • 1970-01-01
      相关资源
      最近更新 更多