背景
我在 SO 和许多流行的博客中看到了多个关于 distinct
的必要性的答案和问题。 JPQL 中的关键字 JOIN FETCH
查询和关于PASS_DISTINCT_THROUGH
查询提示。
例如,看这两个问题
和这些博客文章
我缺少什么
现在我的问题是我无法完全理解
distinct
的确切时间。关键字必须包含在 JPQL 查询中。更具体地说,这取决于使用哪种方法来执行查询( getResultList
或 getSingleResult
)。下面是一个例子来阐明我的意思。
我从现在开始写的所有内容都在 Ubuntu Linux 18.04 上进行了测试,使用 Java 8、Hibernate 5.4.13 和内存中的 H2 数据库(版本 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
的 Hibernate 实现用途 getResultList
在窗帘后面,所以我期待 NonUniqueResultException
被抛出。我还简要浏览了 JPA 2.2 规范,但没有提到处理两种方法之间的重复项的区别,并且每个关于此问题的代码示例都使用
getResultList
方法。结论
在我的例子中,我发现
JOIN FETCH
使用 getSingleResult
执行的查询不要遇到我在 部分链接的资源中解释的重复实体问题。背景 .如果上述声明是正确的,则意味着相同的
JOIN FETCH
查询需要 distinct
如果使用 getResultList
执行,但在使用 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
当查询返回多个结果时:
The
NonUniqueResultException
is thrown by the persistence provider whenQuery.getSingleResult
orTypedQuery.getSingleResult
is invoked and there is more than one result from the query. This exception will not cause the current transaction, if one is active, to be marked for rollback.
但是,查看 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
I need someone to explain me if this is expected or if I misunderstood something.
这实际上归结为您如何解释规范
关于java - 我应该在这个 JPQL 查询中包含 distinct 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63410722/