web-dev-qa-db-fra.com

Résultats distincts de la spécification Spring Data JPA qui utilise la jointure

J'ai les Specification suivants que j'utilise pour rechercher des entités Contact liées à certaines entités ManagedApplication. Je passe un Collection<Long> Qui contient les identifiants des entités ManagedApplication que je recherche.

public static Specification<Contact> findByApp(final Collection<Long> appIds) {
    return new Specification<Contact>() {
        @Override
        public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {            
            final Predicate appPredicate = root.join(Contact_.managedApplications)
                .get(ManagedApplication_.managedApplicationId).in(appIds);
        }
    }
}

Je passe cette spécification à la méthode .findAll() de mon PagingAndSoringRepository pour récupérer un Page<Contact> Qui contiendra toutes les entités Contact qui répondent aux critères de recherche.

Voici le Repository.

@Repository
public interface PagingAndSortingContactRepository extends PagingAndSortingRepository<Contact, Long>, JpaSpecificationExecutor<Contact> {    
}

Et voici comment j'appelle la méthode .findAll().

final Page<Contact> contacts = pagingAndSortingContactRepository.findAll(ContactSpecification.findByApp(appIds), pageable);

Cela fonctionne et renvoie toutes les entités Contact qui sont liées à l'une des entités ManagedApplication qui correspondent aux identifiants transmis. Cependant, puisque j'appelle .join() pour rejoindre le Entité Contact avec l'entité ManagedApplication, si une Contact a plusieurs entités ManagedApplication dans la liste des ID d'application, la requête renverra un doublon Contact entités.

Donc, ce que je dois savoir, comment puis-je obtenir uniquement des entités Contact distinctes renvoyées par ma requête en utilisant ce Specification?

Je sais que CriteriaQuery a une méthode .distinct() à laquelle vous pouvez transmettre une valeur booléenne, mais je n'utilise pas l'instance CriteriaQuery dans toPredicate() de ma Specification.

Voici les sections pertinentes de mes métamodèles.

Contact_.Java:

@StaticMetamodel(Contact.class)
public class Contact_ {
    public static volatile SingularAttribute<Contact, String> firstNm;
    public static volatile SingularAttribute<Contact, String> lastNm;
    public static volatile SingularAttribute<Contact, String> emailAddress;
    public static volatile SetAttribute<Contact, ManagedApplication> managedApplications;
    public static volatile SetAttribute<Contact, ContactToStructure> contactToStructures;
}

ManagedApplication_.Java

@StaticMetamodel(ManagedApplication.class)
public class ManagedApplication_ {
    public static volatile SingularAttribute<ManagedApplication, Integer> managedApplicationId;
}
19
Andrew Mairose

Utilisez le paramètre query dans votre méthode toPredicate pour appeler la méthode distincte.

Exemple ci-dessous:

public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> query, CriteriaBuilder cb) {            
    final Predicate appPredicate = root.join(Contact_.managedApplications)
        .get(ManagedApplication_.managedApplicationId).in(appIds);
    query.distinct(true);
    ...
44
Ish