使用Pageable排序时重复

时间:2019-04-01 11:50:11

标签: java spring spring-boot

控制器:

@RequestMapping(path = "/serviceslist", method = RequestMethod.GET)
    public Page<ServiceResponse> getServicesList(
            @RequestParam(defaultValue = "0") Integer page,
            @RequestParam(defaultValue = "10") Integer size,
            @RequestParam(required = false) String search,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String jobs,
            @RequestParam(required = false) Boolean needsPatrol,
            @RequestParam(defaultValue = "createTime") String sort,
            @RequestParam(defaultValue = "asc") String sortDir
    ) {

        ServiceListRequest request = new ServiceListRequest(search, name, jobs, needsPatrol);

        Sort.Direction direction;

        if (sortDir.equals("asc")) {
            direction = Sort.Direction.ASC;
        } else {
            direction = Sort.Direction.DESC;
        }

        return serviceService.getServicesList(request, of(page, size, direction, sort))
                .map(ServiceResponse::new);
    }

服务:

@Entity
@Data
@Table(name = "service")
public class Service {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false, columnDefinition = "serial")
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "price", nullable = false)
    private float price;

    @Column(name = "season", nullable = false, columnDefinition = "season_enum")
    @Enumerated(EnumType.STRING)
    @Type(type = "pgsql_enum")
    private SeasonEnum season;

    @Column(name = "explanation")
    private String explanation;

    @Column(name = "needs_patrol")
    private boolean needsPatrol;

    @Column(name = "image_required")
    private boolean imageRequired;

    @ManyToOne
    @JoinColumn(name = "color_id", nullable = false)
    private Color color;

    @Column(name = "start_date")
    private Date startDate;

    @Column(name = "end_date")
    private Date endDate;

    @OneToMany(mappedBy = "service")
    @JsonIgnore
    private List<ObjectService> objectServices = new ArrayList<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Job.class)
    @JoinTable(name = "service_has_job",
            joinColumns = { @JoinColumn(name = "service_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "job_id", referencedColumnName = "id")}
    )
    private Set<Job> jobs = new HashSet<>();

    @Column(name = "create_time", nullable = false, updatable = false)
    @JsonIgnore
    private Date createTime = new Date();

    @Column(name = "update_time", nullable = false)
    @JsonIgnore
    private Date updateTime = new Date();

    @PreUpdate
    @PrePersist
    public void onCreateOnUpdate() {
        updateTime = new Date();
    }

    public enum SeasonEnum {
        winter, summer, all_year
    }
}

服务可以有很多工作。

我想要实现的是按作业中的第一项对ASC / DESC进行排序,但是目前,如果服务使用以下方式对4个作业进行排序:

localhost:8080/serviceslist?sort=jobs&sortDir=asc

它为我提供了4次服务,但我需要与众不同。 Pageable是否可以删除重复项并解决我的排序问题?

ServiceService getServicesList函数:

public Page<com.bitweb.syda.data.entity.service.Service> getServicesList(ServiceListRequest request, Pageable pageable) {
        Specification<com.bitweb.syda.data.entity.service.Service> spec = where(null);

        if (request.getSearch() != null) spec = spec.and(search(request.getSearch()));
        if (request.getName() != null) spec = spec.and(name(request.getName()));
        if (request.getJobs() != null) spec = spec.and(hasJobs(request.getJobs()));
        if (request.getNeedsPatrol() != null) spec = spec.and(needsPatrol(request.getNeedsPatrol()));

        return serviceRepository.findAll(spec, pageable);
    }

按名称和内容排序可以很好地工作,只是Jobs部分损坏了:/

编辑21.04.2019:对不起,如果我之前不太清楚的话。理想的结果是按字母顺序对第一个作业进行排序,因为作业按完成时间排序。有可能吗?预先感谢!

工作实体:

@Entity
@Data
@Table(name = "job")
public class Job {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false, columnDefinition = "serial")
    private Long id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "explanation")
    private String explanation;

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Competence.class)
    @JoinTable(name = "job_has_competence",
            joinColumns = { @JoinColumn(name = "job_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "competence_id", referencedColumnName = "id")}
    )
    private Set<Competence> competences = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Material.class)
    @JoinTable(name = "job_has_material",
            joinColumns = { @JoinColumn(name = "job_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "material_id", referencedColumnName = "id")}
    )
    private Set<Material> materials = new HashSet<>();

    @ManyToMany(fetch = FetchType.EAGER, targetEntity = Mechanism.class)
    @JoinTable(name = "job_has_mechanism",
            joinColumns = { @JoinColumn(name = "job_id", referencedColumnName = "id")},
            inverseJoinColumns = { @JoinColumn(name = "mechanism_id", referencedColumnName = "id")}
    )
    private Set<Mechanism> mechanisms = new HashSet<>();

    @Column(name = "create_time", nullable = false, updatable = false)
    @JsonIgnore
    private Date createTime = new Date();

    @Column(name = "update_time", nullable = false)
    @JsonIgnore
    private Date updateTime = new Date();

    @PreUpdate
    @PrePersist
    public void onCreateOnUpdate() {
        updateTime = new Date();
    }

}

Pic

3 个答案:

答案 0 :(得分:1)

您可以这样做

public Page<com.bitweb.syda.data.entity.service.Service> getServicesList(ServiceListRequest request, Pageable pageable) {
        Specification<com.bitweb.syda.data.entity.service.Service> spec = (root, query, builder) -> {
            //you can do any check here if you want with the join and check all the search parameters here if you want
            //Join<Object, Object> jobs = root.join("jobs");
            // also set query to get distinc values
            query.distinct(true);
            return null;
        };

        if (request.getSearch() != null) spec = spec.and(search(request.getSearch()));
        if (request.getName() != null) spec = spec.and(name(request.getName()));
        if (request.getJobs() != null) spec = spec.and(hasJobs(request.getJobs()));
        if (request.getNeedsPatrol() != null) spec = spec.and(needsPatrol(request.getNeedsPatrol()));

        return serviceRepository.findAll(spec, pageable);
    }

答案 1 :(得分:1)

您必须设置与众不同的内容,还要检查查询返回类型:

长将被要求进行分页计数查询

Specification<com.bitweb.syda.data.entity.service.Service> spec = (root, cq, cb) -> {
    if(!Long.class.isAssignableFrom(cq.getResultType())) {
        cq.distinct(true);
    }
    //else {
         //create a query for count case if needed 
    //}
    return null;
};

修改答案:

在这种情况下,考虑到updateTime可能指示作业已排序,建议您执行以下操作:

Specification<com.bitweb.syda.data.entity.service.Service> spec = (root, cq, cb) -> {
    if(!Long.class.isAssignableFrom(cq.getResultType())) {

        if(sort.contains("jobs")) {
            Join<Service, Job> jobs = root.join("jobs");
            //check for asc or desc
            cq.orderBy(cb.asc(jobs.get("updateTime")));
        }
        cq.distinct(true);
    }
    //else {
         //create a query for count case if needed 
    //}
    return null;
};

欢呼

答案 2 :(得分:0)

您需要具有唯一的订购条件。如果不确定,请添加“ id”作为最低优先级排序标准。

问题是:如果未指定记录级别,则db排序是不确定的。即:如果您指定排序但未定义最后一位(即,如果两行满足相同的排序条件),则您将体验到即使是同一行中的两个相同的查询也将返回相同的结果,但顺序不同。