一对多关系在不使用“distinct”的情况下获取重复的对象。为什么?

2024-04-01

我有两个处于一对多关系的类和一个有点奇怪的 HQL 查询。即使我已经阅读了一些已经发布的问题,但对我来说似乎还不清楚。

Class Department{
   @OneToMany(fetch=FetchType.EAGER, mappedBy="department")
   Set<Employee> employees;
}
Class Employee{
   @ManyToOne
   @JoinColumn(name="id_department")
   Department department;
}

当我使用以下查询时,我得到重复的 Department 对象:

session.createQuery("select dep from Department as dep left join dep.employees");

因此,我必须使用不同的:

session.createQuery("select distinct dep from Department as dep left join dep.employees");

这种行为是预期的吗?与 SQL 相比,我认为这是不寻常的。


这个问题已经详细解释了休眠常见问题解答 https://developer.jboss.org/docs/DOC-15782#jive_content_id_Hibernate_does_not_return_distinct_results_for_a_query_with_outer_join_fetching_enabled_for_a_collection_even_if_I_use_the_distinct_keyword:

首先,您需要了解 SQL 以及 OUTER JOIN 在 SQL 中的工作原理。如果 您没有完全理解和理解 SQL 中的外连接,请勿 继续阅读此常见问题解答项,但请参阅 SQL 手册或教程。 否则你不会理解下面的解释并且你 会在 Hibernate 论坛上抱怨这种行为。典型的 可能返回同一订单的重复引用的示例 目的:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .list();

<class name="Order">           
    <set name="lineItems" fetch="join">
    ...
</class>

List result = session.createCriteria(Order.class)  
                        .list();  

List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();  

所有这些示例都生成相同的 SQL 语句:

SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID   

想知道为什么会有重复吗?看一下SQL 结果集,Hibernate 不会隐藏左侧的这些重复项 外部连接结果的但返回所有重复项 驾驶台。如果数据库中有 5 个订单,并且每个订单 有 3 个行项目,结果集将为 15 行。 Java 结果列表 这些查询将有 15 个元素,全部为 Order 类型。只有5个 订单实例将由 Hibernate 创建,但会重复 SQL 结果集被保留为对这 5 个的重复引用 实例。如果你不明白最后一句话,你需要 阅读 Java 和 Java 上的实例之间的区别 堆和对此类实例的引用。 (为什么是左外连接?如果 您将有一个没有行项目的附加订单,结果集 将有 16 行,右侧填充 NULL,其中该行 项目数据用于其他订单。即使他们没有订单,您也想要订单 行项目,对吗?如果没有,请在 HQL 中使用内部联接获取)。
默认情况下,Hibernate 不会过滤掉这些重复的引用。 有些人(不是你)实际上想要这个。你怎样才能过滤掉它们呢? 像这样:

Collection result = new LinkedHashSet( session.create*(...).list() );  

LinkedHashSet 过滤掉重复的引用(它是一个集合)并且 它保留插入顺序(结果中元素的顺序)。那 太简单了,所以你可以用许多不同的和更困难的方式来做 方法:

List result = session.createCriteria(Order.class)  
                        .setFetchMode("lineItems", FetchMode.JOIN)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();  

 
<class name="Order">  
    ...  
    <set name="lineItems" fetch="join">  
  
List result = session.createCriteria(Order.class)  
                        .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)  
                        .list();  
 
List result = session.createQuery("select o from Order o left join fetch o.lineItems")  
                      .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) // Yes, really!  
                      .list();  
 
List result = session.createQuery("select distinct o from Order o left join fetch o.lineItems").list();       

最后一张很特别。看起来你正在使用 SQL 这里有 DISTINCT 关键字。当然,这不是SQL,这是HQL。这 在本例中,distinct 只是结果转换器的快捷方式。 是的,在其他情况下,HQL 不同的将直接转换为 SQL 清楚的。不是在这种情况下:您无法过滤掉重复项 SQL 级别,产品/连接的本质禁止这样做 - 你想要 重复项,否则您无法获得所需的所有数据。所有这些 当结果集是时,重复项的过滤发生在内存中 整理成对象。为什么结果集也应该很明显 基于行的“限制”操作,例如 setFirstResult(5) 和 setMaxResults(10) 不适用于此类急切的获取查询。 如果将结果集限制为一定数量的行,则会截断 数据随机。有一天 Hibernate 可能会足够聪明,知道如果 您调用 setFirstResult() 或 setMaxResults() 它不应该使用连接, 但是第二个 SQL SELECT。尝试一下,您的 Hibernate 版本可能会 已经足够聪明了。如果不是,写两个查询,一个用于限制 东西,另一个用于渴望获取。你想知道为什么 Criteria 查询的示例没有忽略 fetch="join" 映射中的设置但 HQL 不关心?请阅读下一个常见问题解答项目。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一对多关系在不使用“distinct”的情况下获取重复的对象。为什么? 的相关文章

随机推荐