Hibernate Criteria 使用 FetchType.EAGER 多次返回孩子

新手上路,请多包涵

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

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

这些 Order 也有一个字段 orderStatus ,用于使用以下条件进行过滤:

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

这有效,结果符合预期。

现在 这是我的问题:为什么当我将获取类型明确设置为 EAGER 时, Order 在结果列表中出现多次?

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

我将如何更改我的 Criteria 代码以使用新设置达到相同的结果?

原文由 raoulsson 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 441
2 个回答

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

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

所以实际上它 应该 出现多次。作者 (Gavin King) 在这里 对此进行了很好的解释:它既解释了原因,又解释了如何仍然获得不同的结果


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

Hibernate 不会为为集合启用外连接提取的查询返回不同的结果(即使我使用 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 中使用内部连接获取)。

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

像这样:

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

原文由 Eran Medan 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

 criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

原文由 EJB 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题