休眠条件使用 FetchType 多次返回子项。EAGER

2022-08-31 08:53:05

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

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

这些 s 还有一个字段 ,用于使用以下条件进行过滤:OrderorderStatus

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

这有效,结果符合预期。

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

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

如何更改条件代码才能在新设置下获得相同的结果?


答案 1

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

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

所以实际上它应该多次出现。作者(Gavin King)本人在这里很好地解释了这一:它既解释了为什么,又如何仍然获得不同的结果。


在Hibernate [FAQ][2]中也提到过:

休眠不会为为集合启用外部联接提取的查询返回不同的结果(即使我使用 distinct 关键字)?首先,您需要了解SQL以及OUTER JOIN如何在SQL中工作。如果您不完全理解和理解 SQL 中的外连接,请不要继续阅读此常见问题解答项,而是查阅 SQL 手册或教程。否则,您将无法理解以下解释,并将在Hibernate论坛上抱怨此行为。

可能返回同一 Order 对象的重复引用的典型示例:

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

所有这些示例都生成相同的 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。Hibernate 只会创建 5 个 Order 实例,但 SQL 结果集的副本将保留为对这 5 个实例的重复引用。如果您不理解最后一句话,则需要阅读Java以及Java堆上的实例与对此类实例的引用之间的区别。

(为什么选择左外连接?如果您有一个没有订单项的其他订单,则结果集将为 16 行,右侧填充 NULL,其中订单项数据为其他订单。即使订单没有订单项,您也想要订单,对吧?如果没有,请在 HQL 中使用内部联接提取)。

默认情况下,休眠不会过滤掉这些重复的引用。有些人(不是你)实际上想要这个。如何过滤掉它们?

喜欢这个:

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

答案 2

除了Eran提到的内容之外,获取所需行为的另一种方法是设置结果转换器:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

推荐