使用连接的 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<Contact>
,它将包含所有满足搜索条件的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 规范 - 连接中的不同+按列排序