如何解决我应该在此JPQL查询中包括非重复字词吗? 背景我所缺少的结论附录
背景
我在SO上和许多流行的博客中都看到了关于JPQL distinct
查询中JOIN FETCH
关键字的必要性和PASS_DISTINCT_THROUGH
查询提示的多个答案和问题。
例如,看到这两个问题
以及这些博客文章
- The best way to use the JPQL DISTINCT keyword with JPA and Hibernate
- The DISTINCT pass-through Hibernate Query Hint
- Hibernate Tips: How To Apply DISTINCT to Your JPQL But Not Your SQL Query
我所缺少的
现在,我的问题是我无法完全理解JPQL查询中何时必须确切地包含distinct
关键字。更具体地说,是否取决于执行查询的方法(getResultList
或getSingleResult
)。
下面是一个例子,以阐明我的意思。
从现在开始,我写的所有东西都在Ubuntu Linux 18.04,Java 8,Hibernate 5.4.13和内存H2数据库(1.4.200版)中进行了测试。
假设我有一个Department
实体,该实体与DepartmentDirector
实体具有 lazy 双向一对多关系:
// 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
,而与{{1}执行时,则不需要}}。
如果这是预期的或者我误解了一些东西,我需要有人向我解释。
附录
两个查询的结果:
-
查询使用
getSingleResult
方法运行。我得到了预期的两个重复部门(这仅仅是为了测试查询的行为,为此应使用getResultList
)getSingleResult
-
查询使用
List<Department> resultList = entityManager.createQuery(query,Department.class) .setParameter("uuid",department1.getUuid()) .getResultList(); assertThat(resultList).containsExactly(department1,department1); // passes
方法运行。我希望可以检索相同的重复部门,从而抛出getSingleResult
。取而代之的是,只检索一个部门,一切都很好:NonUniqueResultException
解决方法
有趣的问题。
首先让我指出,getSingleResult()
用于由于其性质总是返回单个结果的查询(意味着:主要是汇总的查询,例如SELECT SUM(e.id) FROM Entity e
) 。根据某些特定于业务领域的规则,您认为的查询应返回单个结果,实际上并没有资格。
话虽如此,JPA规范指出,当查询返回多个结果时,getSingleResult()
应该抛出NonUniqueResultException
:
当调用
NonUniqueResultException
或Query.getSingleResult
时,持久性提供程序将抛出TypedQuery.getSingleResult
,并且该查询有多个结果。如果当前事务处于活动状态,则此异常不会导致将当前事务标记为回滚。
但是,请查看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提供程序一起测试了您的方案,事实证明:
- Hibernate确实会从
getResultList()
返回重复项,但由于实现getSingleResult()
的特殊方式,不会引发异常 - EclipseLink是唯一一个不会遭受
getResultList()
中的重复结果错误困扰的人,因此getSingleResult()
也不 抛出异常(对我来说,这行为只是合乎逻辑的,但事实证明,这全都是解释问题) - OpenJPA和DataNucleus都从
getResultList()
返回重复的结果,并从getSingleResult()
抛出异常。
Tl; DR
如果这是预期的或者我误解了一些东西,我需要有人向我解释。
这实际上归结为您对规范的解释
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。