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

时间:2015-11-27 08:38:07

标签: hibernate rest jpa filter

我对这些技术还是比较陌生的,我需要一个能够理解做事的好方法。 我有一个Employee实体,我想通过对端点进行GET查询来列出它们。在对字段应用过滤器之后,我必须返回一个Employees页面。目前,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类,但我想要返回一个Predicates的列表/数组,而不仅仅是一个。因此,覆盖的默认方法返回单个谓词。如何设法从该实体获取多个谓词?

感谢您提供任何暗示,帮助和过去的经验。

2 个答案:

答案 0 :(得分:3)

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

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

然后,我创建了一个实现规范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;
}

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

答案 1 :(得分:0)

也可以使用规格来完成。看起来更加干净和正确。请检查这篇文章: https://blog.tratif.com/2017/11/23/effective-restful-search-api-in-spring/

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

install.packages(c("cowplot","googleway", "ggplot2", "ggrepel",
               "ggspatial", "libwgeom", "sf", "rnaturalearth", "rnaturalearthdata", 
"rgeos"))                 
library("ggplot2")  
theme_set(theme_bw())
library("sf")
library("rnaturalearth")
library("rnaturalearthdata")
library(rgeos)
world <- ne_countries(scale = "medium", returnclass = "sf")
class(world)