【问题标题】:JPA: please help understanding "join fetch"JPA:请帮助理解“join fetch”
【发布时间】:2009-02-26 22:57:04
【问题描述】:

我有以下实体结构:业务 --> 活动 --> 促销,其中一个企业可以有很多活动,一个活动可以有很多促销。两个一对多关系都被声明为 LAZY。在我的代码中的一个地方,我需要急切地从业务中获取这两个集合,所以我这样做了:

    Query query = entityManager.createQuery("select b from Business b " +
            "left join fetch b.campaigns c " +
            "left join fetch c.promotions where b.id=:id");
query.setParameter("id", b.getId());
business = (Business) query.getResultList().get(0);

但是,查询返回一个结果列表,其中包含 4 个业务对象,所有 4 个对象都引用同一个业务实例。在我的数据库中,该商家有 3 个广告系列,并且所有 3 个广告系列都有 3 个促销活动。

我有两个问题:

  1. 一开始,我使用List来包含多个方面的关系,但是当程序运行时,我得到“org.hibernate.HibernateException:不能同时获取多个包”异常。然后我用谷歌搜索了这个异常,看起来我必须使用 Set,而不是 List。所以我将集合更改为 Set 并且它起作用了。谁能告诉我为什么 List 在这种情况下不起作用?

  2. 我希望查询返回一个结果,因为它查询的是主键 id,因此应该只返回一个结果。但事实证明,它在 List 中返回 4 个实例。这是一个问题吗?或者这是预期的行为?

任何帮助将不胜感激。

【问题讨论】:

    标签: hibernate orm jpa


    【解决方案1】:

    生成的 sql 如下所示:

    select * from Business b 
    left outer join campaigns c on c.business_id = b.id
    left join promotions  p on p.campaign_id = c.id
    where b.id=:id
    

    Hibernate 内部只有一个业务实例,但重复项将保留在结果集中。这是预期的行为。您需要的行为可以通过使用 DISTINCT 子句或使用 LinkedHashSet 过滤结果来实现:

    Collection result = new LinkedHashSet(query.getResultList());
    

    这将只返回唯一的结果,保留插入顺序。

    每当您尝试以有序的方式急切地获取多个集合(可能是重复的项目)时,就会发生“org.hibernate.HibernateException:无法同时获取多个包”。如果您考虑生成的 SQL,这确实有点道理。 Hibernate 无法知道重复对象是由连接引起的还是由子表中的实际重复数据引起的。查看this 以获得很好的解释。

    【讨论】:

      【解决方案2】:
      1. 由于笛卡尔积,List 可能有冗余元素,而人们并没有意识到这一点,所以 hibernate 的家伙开始抛出这个错误,迫使人们使用 Set 而不是 List 来避免这个问题。来源1

      【讨论】:

        【解决方案3】:
        1. 我假设如果你使用 List,hibernate 会假设顺序很重要,它会从返回的行中推断出元素的顺序,但是如果你加入另一个表,hibernate 会多次获取每个活动这混淆了休眠。同样,我只是假设这个

        2. 这是预期行为 使用 RootEntityResultTransformer 获取单个根实体:http://www.hibernate.org/hib_docs/v3/api/org/hibernate/transform/RootEntityResultTransformer.html

        【讨论】:

          【解决方案4】:

          另一种替代使用 Set 的解决方法是使用两个查询来获取数据:- 第一个用于加载活动及其相关的业务促销活动。然后使用 fetch 加载商家及其广告系列

              Query query1 = entityManager.createQuery("select c from Business b join b.campaigns c left join fetch c.promotions where b.id=:id");
              query1.setParameter("id", b.getId());
              query1.getResultList();
          
              Query query2 = entityManager.createQuery("select b from Business b left join fetch       b.campaigns where b.id=:id");
              query2.setParameter("id", b.getId());
              business = (Business) query2.getResultList().get(0);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-18
            • 2020-12-29
            • 1970-01-01
            • 2013-05-06
            • 1970-01-01
            相关资源
            最近更新 更多