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

Posted

技术标签:

【中文标题】使用连接的 Spring Data JPA 规范的不同结果【英文标题】:Distinct results from Spring Data JPA Specification that uses join 【发布时间】:2016-01-06 15:09:54 【问题描述】:

我有以下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&lt;Contact&gt;,它将包含所有满足搜索条件的Contact 实体。

这里是Repository

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

这就是我调用.findAll() 方法的方式。

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

这有效并返回与传入的 id 对应的任何 ManagedApplication 实体相关联的所有 Contact 实体。但是,由于我调用 .join() 以将 Contact 实体与 @ 987654339@实体,如果一个Contact在app id列表中有多个ManagedApplication实体,那么查询将返回重复的Contact实体。

所以我需要知道的是,我如何才能使用此 Specification 仅从我的查询中返回不同的 Contact 实体?

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

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

联系方式_.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;

【问题讨论】:

【参考方案1】:

toPredicate 方法中使用 query 参数来调用 distinct 方法。

示例如下:

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

【讨论】:

感谢您提供此解决方案,效果很好!此外,为了澄清,您必须将query.distinct(true) 添加到需要此不同语句的每个谓词中。仅将此语句添加到任何谓词并使其适用于整个查询是不够的.. 这是如何工作的?我的意思是,如果您使用简单的 mysql 语法来连接一些形成多对多关系的表,以便您可以选择左表中在右表的列中具有特定值的行,尽管您使用 SELECT DISTINCT如果左表中的一行与右表中的多行相关,您仍然会得到重复的结果。 开发人员似乎在Specification 中操作query 参数是discouraged 和unsupported。但我没有更好的解决方案。【参考方案2】:

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

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

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

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

【讨论】:

以上是关于使用连接的 Spring Data JPA 规范的不同结果的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data JPA 规范 - 连接中的不同+按列排序

Spring Data JPA方法定义规范

Spring Data JPA:使用连接表进行排序和分页

处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型

如何在 spring data jpa 中使用预测和规范?

Spring Data JPA - 规范和 Querydsl