一对多关系在不使用“不同”的情况下获得重复的对象。为什么? [英] One-To-Many relationship gets duplicate objects whithout using "distinct".Why?

查看:119
本文介绍了一对多关系在不使用“不同”的情况下获得重复的对象。为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一对一关系中的2个类和一个有点奇怪的HQL查询。即使我已经阅读了一些已发布的问题,但对我而言似乎并不清楚。

 班级部门{
@ OneToMany(fetch = FetchType.EAGER,mappedBy =department)
Set< Employee>雇员;
}
类员工{
@ManyToOne
@JoinColumn(name =id_department)
部门部门;
}

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


$ b

session.createQuery(从Dep选择dep dep left join dep.employees);



因此,我必须使用distinct:

session.createQuery(加入dep.employees);



这种行为是否是预期的行为?我认为这是不寻常的,因为它与SQL进行了比较。 解决方案

这个问题在


首先,您需要了解SQL以及OUTER JOIN在SQL中的工作方式。如果
没有完全理解和理解SQL中的外连接,请不要
继续阅读这个FAQ项目,但请参阅SQL手册或教程。
否则你不会理解下面的解释,你
会在Hibernate论坛上抱怨这种行为。典型的
例子可能会返回相同订单
对象的重复引用:



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

< class name =Order>
< set name =lineItemsfetch =join>
...
< / class>

列表结果= session.createCriteria(Order.class)
.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不会过滤这些重复引用默认情况下。
有些人(不是你)实际上需要这个。你怎么能过滤掉它们?
像这样:

 收集结果= new LinkedHashSet(session.create *(...)。list() ); 

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



<$ p $
.setFetchMode(lineItems,FetchMode.JOIN)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
。列表结果= session.createCriteria(Order.class)名单();


< class name =Order>
...
< set name =lineItemsfetch =join>

列表结果= 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)//是的,真的!
.list();

列表结果= session.createQuery(从订单中选择不同的o,左连接读取o.lineItems)。




最后一个很特别。看起来你在这里使用SQL
DISTINCT关键字。当然,这不是SQL,这是HQL。在这种情况下,
不同仅仅是结果转换器的快捷方式。
是的,在其他情况下,HQL不同将直接转换为SQL
DISTINCT。在这种情况下,不能过滤掉
SQL级别的重复项,产品/联接的本质是禁止这种情况 - 您希望
是重复项,否则您将无法获取所需的全部数据。当结果集被编组到对象中时,所有
重复过滤都发生在内存中。同样显而易见的是,为什么结果集
基于行的限制操作(如setFirstResult(5)和
setMaxResults(10))不适用于这些热切的提取查询。
如果将结果集限制到特定行数,则随机切断
数据。有一天Hibernate可能足够聪明,知道如果你调用setFirstResult()或setMaxResults()它应该不使用联接,
而是另一个SQL SELECT,
。试试吧,你的Hibernate版本可能已经足够聪明了。如果没有,写两个查询,一个用于限制
的东西,另一个用于提取。你想知道为什么具有Criteria查询的
例子没有忽略映射中的fetch =join
设置,但HQL并不在意?阅读下一个常见问题项目。



I have 2 classes in a one-to-many relationship and a HQL query that is a bit strange. Even if I have read some questions already posted, it does not seem clear to me.

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

When I use the following query I get duplicates Department objects:

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

Thus, I have to use distinct:

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

Is this behaviour an expected one? I consider this unusual, as comparing it with SQL.

解决方案

This question is thoroughly explained on Hibernate FAQ:

First, you need to understand SQL and how OUTER JOINs work in SQL. If you do not fully understand and comprehend outer joins in SQL, do not continue reading this FAQ item but consult a SQL manual or tutorial. Otherwise you will not understand the following explanation and you will complain about this behavior on the Hibernate forum. Typical examples that might return duplicate references of the same Order object:

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();  

All of these examples produce the same SQL statement:

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

Want to know why the duplicates are there? Look at the SQL resultset, Hibernate does not hide these duplicates on the left side of the outer joined result but returns all the duplicates of the driving table. If you have 5 orders in the database, and each order has 3 line items, the resultset will be 15 rows. The Java result list of these queries will have 15 elements, all of type Order. Only 5 Order instances will be created by Hibernate, but duplicates of the SQL resultset are preserved as duplicate references to these 5 instances. If you do not understand this last sentence, you need to read up on Java and the difference between an instance on the Java heap and a reference to such an instance. (Why a left outer join? If you'd have an additional order with no line items, the result set would be 16 rows with NULL filling up the right side, where the line item data is for other order. You want orders even if they don't have line items, right? If not, use an inner join fetch in your HQL).
Hibernate does not filter out these duplicate references by default. Some people (not you) actually want this. How can you filter them out? Like this:

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

A LinkedHashSet filteres out duplicate references (it's a set) and it preserves insertion order (order of elements in your result). That was too easy, so you can do it in many different and more difficult ways:

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();       

The last one is special. It looks like you are using the SQL DISTINCT keyword here. Of course, this is not SQL, this is HQL. This distinct is just a shortcut for the result transformer, in this case. Yes, in other cases an HQL distinct will translate straight into a SQL DISTINCT. Not in this case: you can not filter out duplicates at the SQL level, the very nature of a product/join forbids this - you want the duplicates or you don't get all the data you need. All of this filtering of duplicates happens in-memory, when the resultset is marshalled into objects. It should be also obvious why resultset row-based "limit" operations, such as setFirstResult(5) and setMaxResults(10) do not work with these kind of eager fetch queries. If you limit the resultset to a certain number of rows, you cut off data randomly. One day Hibernate might be smart enough to know that if you call setFirstResult() or setMaxResults() it should not use a join, but a second SQL SELECT. Try it, your version of Hibernate might already be smart enough. If not, write two queries, one for limiting stuff, the other for eager fetching. Do you want to know why the example with the Criteria query did not ignore the fetch="join" setting in the mapping but HQL didn't care? Read the next FAQ item.

这篇关于一对多关系在不使用“不同”的情况下获得重复的对象。为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆