Spring Boot REST WebService + JPA:可分页和过滤器

Ale*_*lex 4 rest hibernate jpa filter

我对这些技术相当陌生,我需要一个推销来理解做事的好方法。我有一个 Employee 实体,我想通过对端点进行 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;
}
Run Code Online (Sandbox Code Playgroud)

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

我的存储库方法:

Page<Employee> findAllByCompanyIdAndFirstnameLikeAndLastnameLike(Long idCompany, String firstname, String lastname, Pageable pageable);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Ale*_*lex 8

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

Page<Employee> findAll(Specification<Employee> spec, Pageable pageable);
Run Code Online (Sandbox Code Playgroud)

然后,我创建了一个实现规范 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()]));
    }
}
Run Code Online (Sandbox Code Playgroud)

在此之后,在我的 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;
}
Run Code Online (Sandbox Code Playgroud)

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