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

标签 java hibernate jpa jpql

背景
我在 SO 和许多流行的博客中看到了多个关于 distinct 的必要性的答案和问题。 JPQL 中的关键字 JOIN FETCH查询和关于PASS_DISTINCT_THROUGH查询提示。
例如,看这两个问题

  • How does DISTINCT work when using JPA and Hibernate
  • Select DISTINCT on JPA

  • 和这些博客文章
  • 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

  • 我缺少什么
    现在我的问题是我无法完全理解 distinct 的确切时间。关键字必须包含在 JPQL 查询中。更具体地说,这取决于使用哪种方法来执行查询( getResultListgetSingleResult )。
    下面是一个例子来阐明我的意思。
    我从现在开始写的所有内容都在 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 when Query.getSingleResult or TypedQuery.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 提供程序测试了您的场景,结果是:
  • Hibernate 确实从 getResultList() 返回重复项,但不会因为特殊的方式抛出异常 getSingleResult()已实现
  • EclipseLink 是唯一一个不受 getResultList() 中重复结果错误影响的工具。因此,getSingleResult()也不会抛出异常(对我来说,这种行为只是合乎逻辑的,但事实证明,这完全是一个解释问题)
  • OpenJPA 和 DataNucleus 都返回来自 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/

    相关文章:

    java - 从 Hibernate 映射文件绘制 ERD

    java - 为什么在运行 JPA 时出现此异常?

    JOptionPane 上的 Java 阶乘

    java - 如何阻止 Firefox 在数据刷新时要求重新发送?

    java - Hibernate HQL 问题期望找到 IDENT "*"

    java - 我对 Hibernate 感到困惑?

    java - 我可以动态修改 Hibernate/JPA 属性吗?

    jpa - 使用 jpa 条件查询获取列的最大值

    java - Java 中的 Getter 方法

    java - 使用 GSON 在 Java 中解析 JSON