[英]Should I include distinct in this JPQL query?
我在 SO 和许多流行的博客中看到了多个答案和问题,这些答案和问题涉及 JPQL JOIN FETCH
查询中distinct
关键字的必要性以及PASS_DISTINCT_THROUGH
查询提示。
比如看这两个问题
和这些博客文章
现在我的问题是我无法完全理解何时必须将distinct
关键字包含在 JPQL 查询中。 更具体地说,它是否取决于用于执行查询的方法( getResultList
或getSingleResult
)。
下面是一个例子来阐明我的意思。
Everything I am writing from now on was tested on Ubuntu Linux 18.04, with Java 8, Hibernate 5.4.13 and an in-memory H2 database (version 1.4.200).
假设我有一个Department
实体,它与DepartmentDirector
实体具有惰性双向一对多关系:
// Department.java
@Entity
public class Department {
// ...
private Set<DepartmentDirector> directors;
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
public Set<DepartmentDirector> getDirectors() {
return directors;
}
// ...
}
// DepartmentDirector.java
@Entity
public class DepartmentDirector {
// ...
private Department department
@ManyToOne
@JoinColumn(name = "department_fk")
public Department getDepartment() {
return department;
}
// ...
}
假设我的数据库当前包含一个部门 ( department1
) 和两个与之关联的主管。
现在我想通过它的 uuid(主键)以及它的所有主管来检索部门。 这可以通过以下JOIN FETCH
JPQL 查询来完成:
String query = "select department from Department department left join fetch "
+ "department.directors where department.uuid = :uuid";
由于前面的查询使用子集合的join fetch
,我希望它在发出时返回两个重复的部门:但是,这只发生在使用getResultList
方法的查询而不是使用getSingleResult
方法时。 这在某种程度上是合理的,但我发现 getSingleResult 的getSingleResult
实现在幕后使用getResultList
,所以我预计会抛出NonUniqueResultException
。
我还简要浏览了 JPA 2.2 规范,但没有提到处理两种方法之间的重复项的区别,并且每个与此问题有关的代码示例都使用getResultList
方法。
在我的示例中,我发现使用getSingleResult
执行的JOIN FETCH
查询不会遇到我在背景部分链接的资源中解释的重复实体问题。
如果上述声明是正确的,则意味着如果使用getResultList
执行相同的JOIN FETCH
查询需要distinct
,但使用getSingleResult
执行时不需要它。
如果这是预期的或我误解了什么,我需要有人向我解释。
两个查询的结果:
使用getResultList
方法运行查询。 我得到了两个重复的部门(这样做只是为了测试查询的行为,应该使用getSingleResult
代替):
List<Department> resultList = entityManager.createQuery(query, Department.class).setParameter("uuid", department1.getUuid()).getResultList(); assertThat(resultList).containsExactly(department1, department1); // passes
使用getSingleResult
方法运行查询。 我希望检索到相同的重复部门,因此会抛出NonUniqueResultException
。 相反,检索到一个部门并且一切正常:
Department singleResult = entityManager.createQuery(query, Department.class).setParameter("uuid", department1.getUuid()).getSingleResult(); assertThat(singleResult).isEqualTo(department1); // passes
有趣的问题。
首先,让我指出getSingleResult()
用于由于其性质而总是返回单个结果的查询(意思是:主要是聚合查询,例如SELECT SUM(e.id) FROM Entity e
)。 您认为基于某些业务领域特定规则的查询应该返回单个结果,但实际上并不符合条件。
话虽如此,JPA 规范声明getSingleResult()
应该在查询返回多个结果时抛出NonUniqueResultException
:
当调用
Query.getSingleResult
或TypedQuery.getSingleResult
并且查询有多个结果时,持久性提供程序会抛出NonUniqueResultException
。 此异常不会导致当前事务(如果处于活动状态)被标记为回滚。
但是,查看 Hibernate 实现:
@Override
public R getSingleResult() {
try {
final List<R> list = list();
if ( list.size() == 0 ) {
throw new NoResultException( "No entity found for query" );
}
return uniqueElement( list );
}
catch ( HibernateException e ) {
if ( getProducer().getFactory().getSessionFactoryOptions().isJpaBootstrap() ) {
throw getExceptionConverter().convert( e );
}
else {
throw e;
}
}
}
public static <R> R uniqueElement(List<R> list) throws NonUniqueResultException {
int size = list.size();
if ( size == 0 ) {
return null;
}
R first = list.get( 0 );
for ( int i = 1; i < size; i++ ) {
if ( list.get( i ) != first ) {
throw new NonUniqueResultException( list.size() );
}
}
return first;
}
事实证明,Hibernate 对“不止一个结果”的解释似乎是“不止一个独特的结果”。
事实上,我用所有 JPA 提供者测试了你的场景,结果是:
getResultList()
返回重复项,但由于实现getSingleResult()
的特殊方式而不会引发异常getResultList()
中重复结果错误的影响,因此, getSingleResult()
也不会引发异常(对我来说,这种行为只是合乎逻辑的,但事实证明,这就是全部解释问题)getResultList()
返回重复的结果,并从getSingleResult()
抛出异常Tl;DR
如果这是预期的或我误解了什么,我需要有人向我解释。
这真的归结为您如何解释规范
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.