我应该在此JPQL查询中包括非重复字词吗? 背景我所缺少的结论附录

如何解决我应该在此JPQL查询中包括非重复字词吗? 背景我所缺少的结论附录

背景

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

例如,看到这两个问题

以及这些博客文章

我所缺少的

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

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

从现在开始,我写的所有东西都在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}执行时,则不需要}}。

如果这是预期的或者我误解了一些东西,我需要有人向我解释。


附录

两个查询的结果:

  1. 查询使用getSingleResult方法运行。我得到了预期的两个重复部门(这仅仅是为了测试查询的行为,为此应使用getResultList

    getSingleResult
  2. 查询使用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

当调用NonUniqueResultExceptionQuery.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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-