Java 我应该在这个JPQL查询中包含distinct吗? 背景
我在这里和许多流行的博客上看到了关于JPQLJava 我应该在这个JPQL查询中包含distinct吗? 背景,java,hibernate,jpa,jpql,Java,Hibernate,Jpa,Jpql,我在这里和许多流行的博客上看到了关于JPQLJOIN FETCH查询中使用distinct关键字的必要性以及关于PASS\u distinct\u THROUGH查询提示的多个答案和问题 例如,请看这两个问题 还有这些博客帖子 我错过了什么 现在我的问题是,我无法完全理解JPQL查询中何时必须包含distinct关键字。更具体地说,如果它取决于用于执行查询的方法(getResultList或getSingleResult) 下面是一个例子来阐明我的意思 从现在开始,我写的所有东
JOIN FETCH
查询中使用distinct
关键字的必要性以及关于PASS\u distinct\u THROUGH
查询提示的多个答案和问题
例如,请看这两个问题
distinct
关键字。更具体地说,如果它取决于用于执行查询的方法(getResultList
或getSingleResult
)
下面是一个例子来阐明我的意思
从现在开始,我写的所有东西都在UbuntuLinux18.04、Java8、Hibernate5.4.13和内存H2数据库(版本1.4.200)上进行了测试
假设我有一个部门
实体,它与部门主管
实体有一个惰性双向一对多关系:
//Department.java
@实体
公共课系{
// ...
私人董事;
@OneToMany(mappedBy=“department”,fetch=FetchType.LAZY)
公共集getDirectors(){
返回董事;
}
// ...
}
//DepartmentDirector.java
@实体
公共课系主任{
// ...
私人部门
@许多酮
@JoinColumn(name=“department\u fk”)
公共部门{
退货部;
}
// ...
}
假设我的数据库当前包含一个部门(department1
)和两个与其关联的主管
现在,我想通过uuid(主键)及其所有主管检索部门。这可以通过以下joinfetch
JPQL查询完成:
String query=“从部门左侧加入获取中选择部门”
+“department.directors,其中department.uuid=:uuid”;
由于前面的查询对子集合使用了join fetch
,因此我希望它在发出时返回两个重复的部门:但是,这仅在使用getResultList
方法进行查询时发生,而在使用getSingleResult
方法时不会发生。这在某种程度上是合理的,但我发现getSingleResult
的Hibernate实现在幕后使用了getResultList
,因此我希望抛出ununiqueresultexception
我还简要介绍了JPA2.2规范,但没有提到两种方法在处理重复项方面的区别,并且与此问题相关的每个代码示例都使用getResultList
方法
结论
在我的示例中,我发现使用getSingleResult
执行的JOIN-FETCH
查询不会遇到我在Background一节中链接的参考资料中解释的复制实体问题
如果上述声明正确,则意味着如果使用getResultList
执行相同的JOIN-FETCH
查询,则需要distinct
,但使用getSingleResult
执行时不需要它
我需要有人向我解释,如果这是预期的或如果我误解了什么
附录 两项查询的结果:
getResultList
方法运行查询。我得到了两个重复的部门(这只是为了测试查询的行为,应该使用getSingleResult
):
List resultList=entityManager.createQuery(查询,Department.class)
.setParameter(“uuid”,department1.getUuid())
.getResultList();
资产(结果列表)。实际包含(部门1,部门1);//通行证
getSingleResult
方法运行查询。我希望检索相同的重复部门,从而抛出ununiqueresultexception
。取而代之的是,只检索一个部门,一切正常:
Department singleResult=entityManager.createQuery(查询,Department.class)
.setParameter(“uuid”,department1.getUuid())
.getSingleResult();
资产(单一结果)。isEqualTo(部门1);//通行证
getSingleResult()
用于由于其性质而总是返回单个结果的查询(意思是:大多数聚合查询,如SELECT SUM(e.id)FROM Entity e
)。您认为基于某个特定于业务领域的规则的查询应该返回一个结果,但实际上并不符合条件
也就是说,JPA规范规定当查询返回多个结果时,getSingleResult()
应该抛出ununiqueresultException
:
当调用Query.getSingleResult
或TypedQuery.getSingleResult
时,持久性提供程序将引发UnuniqueresultException
,并且查询中有多个结果。如果当前事务处于活动状态,此异常不会导致标记为回滚
但是,看看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;
}
@覆盖
公共R getSingleResult(){
试一试{
最终列表=列表();
if(list.size()==0){
抛出新的NoResultException(“未找到用于查询的实体”);
}
返回uniquelement(列表);
}
捕获(休眠异常e){
if(getProducer().getFactory().getSessionFactoryOptions().isJpaBootstrap()){
抛出getExceptionConverter().convert(e);
}
否则{
投掷e;
}
}
}