2
votes

Dupliquer lors du tri à l'aide de Pageable

Controller:

@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();
    }

}

Service:

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);
    }

Un service peut avoir plusieurs tâches.

Ce que j'essaie de faire, c'est de trier ASC / DESC par le premier élément des travaux, mais actuellement, si un service a, disons 4 travaux, après le tri en utilisant:

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

il me donne le service 4 fois, mais j'ai besoin qu'il soit distinct. Est-ce que pageable a un moyen de supprimer les doublons et de résoudre mon problème de tri?

Fonction ServiceService getServicesList:

@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
    }
}

Le tri par nom et par truc fonctionne bien, juste le la partie jobs est cassée: /

Edit 21.04.2019: Je suis désolé si je n'étais pas vraiment clair avant. Le résultat idéal serait de trier par ordre alphabétique du premier travail, puisque les travaux sont dans l'ordre où ils sont terminés. Est-ce possible d'une manière ou d'une autre? Merci d'avance!

Entité d'emploi:

@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);
    }

Pic


2 commentaires

que contient la liste getService? afficher votre requête réelle


@MaciejKowalski a édité mon message


3 Réponses :


1
votes

Vous pouvez faire quelque chose comme ceci

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);
    }


2 commentaires

Après avoir utilisé ceci, le tri par tâches me donne cette erreur: PSQLException: ERROR: pour SELECT DISTINCT, les expressions ORDER BY doivent apparaître dans la liste de sélection Le tri par d'autres champs fonctionne bien: /


Lors de la pagination, Spring émet deux requêtes: une pour les résultats et une pour le nombre afin de déterminer le nombre total de pages. L'erreur est probablement causée par la requête de comptage. Essayez la solution recommandée par @madz



2
votes

Vous devez définir un type de retour de requête distinct et également vérifier:

Long sera appelé pour les requêtes de nombre de pagination

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;
};

Réponse pour la modification:

Dans ce cas, considérant que updateTime pourrait indiquer la commande des travaux, je vous suggère de faire ce qui suit:

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;
};

Cheers


3 commentaires

Hé et merci d'avoir répondu! Cela me donne les mêmes expressions SELECT DISTINCT, ORDER BY doivent apparaître dans la liste de sélection - erreur. Pouvez-vous vérifier ma dernière modification sur la question?


Hey. Aucun problème. Qu'entendez-vous par les travaux commandés? Aucun ordre n'est conservé dans Set. Vous devez définir une propriété d'horodatage pour l'entité Job et trier par ce champ. Pouvons-nous voir la définition de l'entité Job?


J'ai ajouté l'entité d'emploi à la publication avec une photo de ma table de données. Les colonnes vont: nom, emplois, besoinsPatrol. J'aimerais beaucoup si je pouvais trier par le premier emploi de la colonne.



1
votes

Vous devez avoir un critère de commande unique. Ajoutez «id» comme critère de tri le moins prioritaire en cas de doute.

Le problème est: le tri de la base de données n'est pas défini s'il n'est pas spécifié au niveau de l'enregistrement. C'est-à-dire: si vous spécifiez le tri mais laissez le dernier bit non défini (c'est-à-dire si deux lignes remplissent les mêmes critères de tri), vous constaterez que même deux requêtes identiques dans une ligne renverront le même résultat mais dans un ordre différent.


0 commentaires