JPA:请帮助理解“加入获取”

时间:2009-02-26 22:57:05

标签: hibernate orm jpa

我有以下实体结构:业务 - >广告系列 - >促销活动,其中一个商家可以有多个广告系列,一个广告系列可以有多个促销活动。一对多关系都被宣布为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个Business对象,所有4个对象都引用同一个Business实例。在我的数据库中,此商家下面有3个广告系列,所有3个广告系列都有3个广告系列。

我有两个问题:

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

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

  3. 非常感谢任何帮助。

4 个答案:

答案 0 :(得分:20)

生成的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只有一个Business实例,但重复项将保留在结果集中。这是预期的行为。您需要的行为可以通过使用DISTINCT子句或使用LinkedHashSet来过滤结果来实现:

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

将仅返回唯一结果,保留插入顺序。

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

答案 1 :(得分:3)

  1. 列表可能有多余的元素,因为笛卡尔积和人们都没有意识到这一点,所以hibernate人开始抛出这个错误迫使人们使用Set而不是List来避免这个问题。来源1

答案 2 :(得分:1)

  1. 我假设你使用List,hibernate假定顺序很重要,它会从返回的行中推断出元素的顺序,但如果你加入一个nother表,hibernate会多次获得每个广告系列这让hibernate感到困惑。再一次,我只是假设这个

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

答案 3 :(得分:0)

另一种解决方法而不是使用Set是使用两个查询来获取数据: - 第一个加载广告系列及其相关的业务促销。然后使用提取

加载商家及其广告系列
    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);