Hibernate Criteria 使用 FetchType.EAGER 多次返回子项 [英] Hibernate Criteria returns children multiple times with FetchType.EAGER

查看:25
本文介绍了Hibernate Criteria 使用 FetchType.EAGER 多次返回子项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 Order 类,它有一个 OrderTransactions 列表,我用一对多的 Hibernate 映射来映射它,如下所示:

@OneToMany(targetEntity = OrderTransaction.class, 级联 = CascadeType.ALL)公共列表<OrderTransaction>getOrderTransactions() {退货订单交易;}

这些Order还有一个字段orderStatus,用于使用以下标准进行过滤:

public ListgetOrderForProduct(OrderFilter orderFilter) {标准标准 = getHibernateSession().createCriteria(Order.class).add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));返回标准列表();}

这行得通,结果如预期.

现在这是我的问题:为什么,当我将提取类型显式设置为 EAGER 时,Order 是否会多次出现在结果列表?

@OneToMany(targetEntity = OrderTransaction.class,fetch = FetchType.EAGER,cascade = CascadeType.ALL)公共列表<OrderTransaction>getOrderTransactions() {退货订单交易;}

我必须如何更改我的标准代码才能使用新设置达到相同的结果?

解决方案

如果我正确理解您的配置,这实际上是预期的行为.

您在任何结果中都得到相同的 Order 实例,但由于现在您正在与 OrderTransaction 进行连接,因此它必须返回相同数量的结果常规的 sql join 将返回

所以实际上它应该出现多次.作者 (Gavin King) 本人对此进行了很好的解释 "它既解释了原因,也解释了如何仍然获得不同的结果

<小时>在 Hibernate 中也提到了

Hibernate 不会为为集合启用外连接获取的查询返回不同的结果(即使我使用了不同的关键词)?首先,您需要了解 SQL 以及 OUTER JOIN 的工作原理在 SQL 中.如果您不完全理解和理解外连接SQL,不要继续阅读这个常见问题,而是查阅 SQL 手册或教程.否则你不会理解下面的解释你会在 Hibernate 论坛上抱怨这种行为.

可能返回相同引用的重复引用的典型示例订单对象:

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

<小时>

...<set name="lineItems" fetch="join">

<小时>

List result = 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 默认不会过滤掉这些重复的引用.有些人(不是你)实际上想要这个.如何过滤掉它们?

像这样:

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

I have an Order class that has a list of OrderTransactions and I mapped it with a one-to-many Hibernate mapping like so:

@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

These Orders also have a field orderStatus, which is used for filtering with the following Criteria:

public List<Order> getOrderForProduct(OrderFilter orderFilter) {
    Criteria criteria = getHibernateSession()
            .createCriteria(Order.class)
            .add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
    return criteria.list();
}

This works and the result is as expected.

Now here is my question: Why, when I set the fetch type explicitly to EAGER, do the Orders appear multiple times in the resulting list?

@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
    return orderTransactions;
}

How would I have to change my Criteria code to reach the same result with the new setting?

解决方案

This is actually the expected behaviour if I understood your configuration correctly.

You get the same Order instance in any of the results, but since now you are doing a join with the OrderTransaction, it has to return the same amount of results a regular sql join will return

So actually it should apear multiple times. this is explained very well by the author (Gavin King) himself here: It both explains why, and how to still get distinct results


Also mentioned in the Hibernate FAQ :

Hibernate does not return distinct results for a query with outer join fetching enabled for a collection (even if I use the distinct keyword)? 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">


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

这篇关于Hibernate Criteria 使用 FetchType.EAGER 多次返回子项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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