繁体   English   中英

我应该在这个 JPQL 查询中包含 distinct 吗?

[英]Should I include distinct in this JPQL query?

背景

我在 SO 和许多流行的博客中看到了多个答案和问题,这些答案和问题涉及 JPQL JOIN FETCH查询中distinct关键字的必要性以及PASS_DISTINCT_THROUGH查询提示。

比如看这两个问题

和这些博客文章

我错过了什么

现在我的问题是我无法完全理解何时必须将distinct关键字包含在 JPQL 查询中。 更具体地说,它是否取决于用于执行查询的方法( getResultListgetSingleResult )。

下面是一个例子来阐明我的意思。

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执行时不需要它。

如果这是预期的或我误解了什么,我需要有人向我解释。


附录

两个查询的结果:

  1. 使用getResultList方法运行查询。 我得到了两个重复的部门(这样做只是为了测试查询的行为,应该使用getSingleResult代替):

     List<Department> resultList = entityManager.createQuery(query, Department.class).setParameter("uuid", department1.getUuid()).getResultList(); assertThat(resultList).containsExactly(department1, department1); // passes
  2. 使用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.getSingleResultTypedQuery.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 提供者测试了你的场景,结果是:

  • Hibernate 确实从getResultList()返回重复项,但由于实现getSingleResult()的特殊方式而不会引发异常
  • EclipseLink 是唯一一个不受getResultList()中重复结果错误的影响,因此, getSingleResult()不会引发异常(对我来说,这种行为只是合乎逻辑的,但事实证明,这就是全部解释问题)
  • OpenJPA 和 DataNucleus 都从getResultList()返回重复的结果,并从getSingleResult()抛出异常

Tl;DR

如果这是预期的或我误解了什么,我需要有人向我解释。

这真的归结为您如何解释规范

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM