【问题标题】:Spring Boot REST WebService + JPA : pageable and filterSpring Boot REST WebService + JPA:可分页和过滤
【发布时间】:2016-03-01 09:56:12
【问题描述】:

我对这些技术还很陌生,我需要一个推销来了解做事的好方法。 我有一个员工实体,我想通过对端点进行 GET 查询来列出它们。在对字段应用过滤器后,我必须返回一个员工页面。目前,GET 查询上的 Pageable 有效,但不是我的过滤器。

这是我的 REST 端点:

@RequestMapping(value = "/employees",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@Transactional(readOnly = true)
public ResponseEntity<List<EmployeeDTO>> getAllEmployees(Pageable pageable, String filters) throws URISyntaxException, JSONException {

        JSONObject sfilters = null;
        try {
            sfilters = new JSONObject(filters.trim());
        } catch (JSONException e) {
            e.printStackTrace();
        }

        // I wish this on could works, but still have to update it if we add fields to our Employee entity
        Page<Employee> page = employeeRepository.findAllByCompanyIdAndFirstnameLikeAndLastnameLike(
            userService.getUserWithAuthorities().getCompany().getId(),
            sfilters.get("firstname").toString(),
            sfilters.get("lastname").toString(),
            pageable);

        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/employees");
        ResponseEntity<List<EmployeeDTO>> result = new ResponseEntity<>(page.getContent().stream()
            .map(employeeMapper::employeeToEmployeeDTO)
            .collect(Collectors.toCollection(LinkedList::new)), headers, HttpStatus.OK);

        return result;
}

注意:我必须过滤比这更多的字段,但我想让示例尽可能清晰。

我的存储库方法:

Page<Employee> findAllByCompanyIdAndFirstnameLikeAndLastnameLike(Long idCompany, String firstname, String lastname, Pageable pageable);

客户端,它运行良好,我为可分页发送了良好的参数,并将我的过滤器转换为 JSONObject。但现在我需要关于如何在查询中正确生成动态过滤器的建议。我的 JPA 存储库方法不起作用。

我尝试使用谓词,但它没有帮助,因为当我给它一个 Predicate arg 时方法会失败。如果一个或多个与您的 pred 匹配但不确定它们是否用于通过动态查询检索一组项目,则此方法似乎很适合逐项检查。

编辑:我创建了一个实现规范的 EmployeeSpecification 类,但我想返回一个谓词列表/数组,而不仅仅是一个。因此,要覆盖的默认方法返回单个 Predicate。我怎样才能设法从这个实体获得多个谓词?

感谢您的任何提示、帮助和过去的经验分享。

【问题讨论】:

  • 主题被浏览了 13 次。我不得不承认,JPA 问题并不是最想要的科目。我会检查文档并阅读一些内容。

标签: hibernate rest jpa filter


【解决方案1】:

我发现了如何使用谓词来做到这一点。首先,我必须在我的存储库中使用 JPA 方法 findAll :

Page<Employee> findAll(Specification<Employee> spec, Pageable pageable);

然后,我创建了一个实现 Specification Spring Boot 对象的自定义类:

public class EmployeeSpecification implements Specification<Employee> {

    private final JSONObject criteria;
    private List<Predicate> filters;

    public EmployeeSpecification(JSONObject criteria) {
        this.criteria = criteria;
    }

    @Override
    public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Iterator<?> keys = criteria.keys();
        List<Predicate> filters = new ArrayList<>();

        if (criteria.length() != 0) {

            while (keys.hasNext()) {
                String key = (String) keys.next();
                String filterValue = null;

                try {
                    filterValue = criteria.get(key).toString();
                } catch (JSONException e) {
                    e.printStackTrace();
                }

                if (filterValue != null) {
                    filters.add(criteriaBuilder.like(criteriaBuilder.upper(root.<String>get(key)), "%" + filterValue.toUpperCase() + "%"));
                }
            }
        }
        //this is the point : didn't know we could concatenate multiple predicates into one.
        return criteriaBuilder.and(filters.toArray(new Predicate[filters.size()]));
    }
}

之后,在我的 WS 端点方法中,我只需要实例化 EmployeeSpecification调用 JPA findAll 方法,传递我的过滤器 JSON 对象和我的 Pageable 对象:

@RequestMapping(value = "/employees",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
@Transactional(readOnly = true)
public ResponseEntity<List<EmployeeDTO>> getAllEmployees(Pageable pageable, String filters) throws URISyntaxException, JSONException {

    JSONObject sfilters = null;
    try {
        sfilters = new JSONObject(filters.trim());
    } catch (JSONException e) {
        e.printStackTrace();
    }

    EmployeeSpecification spec = new EmployeeSpecification(sfilters);

    Page<Employee> page = employeeRepository.findAll(
        spec,
        pageable);

    HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/employees");
    ResponseEntity<List<EmployeeDTO>> result = new ResponseEntity<>(page.getContent().stream()
        .map(employeeMapper::employeeToEmployeeDTO)
        .collect(Collectors.toCollection(LinkedList::new)), headers, HttpStatus.OK);
    return result;
}

现在我可以发送可分页项目和多个字段过滤器,我能够根据排序、每页数、当前页面和字段过滤器正确检索结果。 非常感谢您的帮助;)(大声笑)

【讨论】:

  • 天哪,我发现我不能将@Param 参数传递给该方法,因为它只接受 Sort 和 Pageable args 类型。所以我无法进行连接查询,向其添加规范和可分页项。
  • 其实可以通过.join()来使用JPA Predicates进行join查询
【解决方案2】:

也可以使用规范来完成。它似乎更清洁和正确。请检查这篇文章: https://blog.tratif.com/2017/11/23/effective-restful-search-api-in-spring/

它还展示了如何解决“加入”问题和其他问题。 一般来说,您可以像这样添加过滤器来查询(取自上面的链接):

@GetMapping
public Page<Customer> findCustomersByFirstName(
    @Or({
        @Spec(path = "names.firstName", params = "name", spec = Like.class),
        @Spec(path = "names.lastName", params = "name", spec = Like.class),
        @Spec(path = "names.nickName", params = "name", spec = Like.class)
    }) Specification<Customer> customerSpec,
    Pageable pageable) {

    return customerRepo.findAll(customerSpec, pageable);
}

【讨论】:

    猜你喜欢
    • 2020-01-25
    • 2021-08-28
    • 2020-10-30
    • 1970-01-01
    • 2019-02-21
    • 2016-02-01
    • 2017-11-24
    • 2016-09-19
    • 2019-01-06
    相关资源
    最近更新 更多