Java 如何将 List<Object> 从 JPA 分页转换为 Page

Java how to transform List<Object> to Page from JPA pagination

我对 Jpa 存储库中的自定义搜索方法有疑问。我实现了这个方法来自定义搜索,在这个方法中,我从我的 SQL 数据库中得到了 List<Car>,我尝试将它转换为来自 JPA 分页和排序的 Page<Car> 对象,但是我的方式我试图这样做是行不通的。这只是分页的错觉,因为在 postman/internet 浏览器中,我看到列表中的所有元素,当我更改端点中的页面和大小值时,没有任何改变。

有人知道如何提供帮助吗?

GitHub 项目:https://github.com/s0bieskii/DemoCarRental

我来自控制器的端点:

@GetMapping("/find")
ResponseEntity<Page<CarDTO>> readAllCarsFiltered(@RequestParam(defaultValue = "0") int page,
                                                 @RequestParam(defaultValue = "5") int size,
                                                 Pageable pageable,
                                                 @RequestParam(required = false) String brand,
                                                 @RequestParam(required = false) String model,
                                                 @RequestParam(required = false) String type,
                                                 @RequestParam(required = false) Integer registrationYear,
                                                 @RequestParam(required = false) String color,
                                                 @RequestParam(required = false) String fuel,
                                                 @RequestParam(required = false) String transmission,
                                                 @RequestParam(required = false) String doors,
                                                 @RequestParam(required = false, defaultValue = "9999999999") double price) {
    return ResponseEntity.ok(carService.customerSearch(pageable,
            brand, model, type, registrationYear, color, fuel, transmission, doors, price));
}

我的服务:

public Page<CarDTO> customerSearch(Pageable pageable, String brand, String model, String type, Integer registrationNumber, String color,
                                   String fuel, String transmission, String doors, double price){

    return carRepository.search(pageable, brand, model, type, registrationNumber, color, fuel, transmission, doors, price)
            .map(car -> carMapper.carToDto(car));
}

我的自定义搜索方法来自 RepositoryImplementation

 @Override
    public Page<Car> search(Pageable pageable, String brand, String model, String type, Integer registrationYear, String color,
                            String fuel, String transmission, String doors, double price) {
        int var = 0;
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Car> query = cb.createQuery(Car.class);
        Root<Car> car = query.from(Car.class);
        query.select(car);
        Predicate predicate = cb.greaterThan(car.get("id"), var);

        if (brand != null) predicate = cb.and(predicate, cb.equal(car.get("brand"), brand));
        if (model != null) predicate = cb.and(predicate, cb.equal(car.get("model"), model));
        if (type != null) predicate = cb.and(predicate, cb.equal(car.get("type"), type));
        if (registrationYear != null)
            predicate = cb.and(predicate, cb.equal(car.get("registrationNumber"), registrationYear));
        if (color != null) predicate = cb.and(predicate, cb.equal(car.get("color"), color));
        if (fuel != null) predicate = cb.and(predicate, cb.equal(car.get("fuel"), Fuel.stringToFuelEnum(fuel)));
        if (transmission != null)
            predicate = cb.and(predicate, cb.equal(car.get("transmission"), Transmission.stringToTransmissionEnum(transmission)));
        if (doors != null) predicate = cb.and(predicate, cb.equal(car.get("doors"), doors));
        if (price != 0) predicate = cb.and(predicate, cb.lessThan(car.get("price"), price));
        //predicate = cb.and(predicate, cb.equal(car.get("available"), available));
        List<Car> carList=entityManager.createQuery(query.where(predicate)).getResultList();

        return new PageImpl<Car>(carList, pageable,carList.size());
    }
}

当您进行自定义查询并希望使用 Pageable 时,您需要实现一个逻辑来为您的结果列表创建分页。在你的情况下你没有这样做所以它可能(你说它不起作用,但没有说什么不起作用所以我只能猜测)只是返回整个结果列表而不应用分页。

一个简单的解决方案是使用 sublist 并将该操作的结果用作 PageImpl 的参数。最大的问题是您仍然在后台从数据库中获取整个结果列表,这会产生不必要的开销。

您还可以使用

调整您的查询
em.createQuery(query.where(predicate))
      .setFirstResult(offset)
     .setMaxResults(limit)
     .getResultList()

现在分页在数据库中完成,好多了。

或者您省去一些麻烦并使用 Springs Specification 接口而不是创建自己的搜索方法。

首先你的 CarRepository 需要扩展 JpaSpecificationExecutor。现在 spring 将为您的存储库生成一个 findAll(Specification, Pageable) 方法。

第二步是创建搜索规范:

public class SearchSpecification implements Specification<Car> {
    private final String brand;
    private final String model
    private final String type
    private final Integer registrationYear
    private final String color
    private final String fuel
    private final String transmission
    private final String doors
    private final double price

    // constructor & maybe getter
    public Predicate toPredicate(Root<Car> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        // Create your search predicate
    }
}

现在您只需拨打 carRepository.findAll(new SearchSpecification (/* parameters */), pageable);,spring 就会为您进行寻呼。