Spring 使用连接的数据 JPA 规范的不同结果

Distinct results from Spring Data JPA Specification that uses join

我有以下 Specification 用于查询与某些 ManagedApplication 实体相关联的任何 Contact 实体。我传入一个 Collection<Long>,其中包含我正在搜索的 ManagedApplication 实体的 ID。

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

我将此规范传递给 PagingAndSoringRepository.findAll() 方法以检索 Page<Contact>,它将包含满足搜索条件的所有 Contact 实体。

这里是 Repository.

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

下面是我如何调用 .findAll() 方法。

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

这有效,return所有 Contact 实体都绑定到与传入的 ID 相对应的任何 ManagedApplication 实体。但是,由于我正在调用 .join() 加入 Contact 实体与 ManagedApplication 实体,如果一个 Contact 在应用程序 ID 列表中有多个 ManagedApplication 实体,则查询将 return 重复 Contact 个实体。

所以我需要知道的是,如何使用此 Specification 从我的查询中仅获得不同的 Contact 个实体 return?

我知道 CriteriaQuery 有一个 .distinct() 方法,您可以将布尔值传递给该方法,但我没有在 toPredicate() 方法中使用 CriteriaQuery 实例我的 Specification.

这是我的元模型的相关部分。

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

在您的 toPredicate 方法中使用 query 参数来调用不同的方法。

示例如下:

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

可以添加一个新的静态方法

public static Specification<Object> distinct() {
    return (root, query, cb) -> {
        query.distinct(true);
        return null;
    };
}

稍后您可以在创建规范时添加的地方

Specification.where(
    YourStaticClassWhereYouCreatedTheUpperMethod.distinct().and(..))