7

我有这些课程:

@Entity
public class Invoice implements Serializable {
    @Id
    @Basic(optional = false)
    private Integer number;

    private BigDecimal value;

    //Getters and setters
}

@Entity
public class InvoiceItem implements Serializable {
    @EmbeddedId
    protected InvoiceItemPK invoiceItemPk;

    @ManyToOne
    @JoinColumn(name = "invoice_number", insertable = false, updatable = false)
    private Invoice invoice;

    //Getters and setters
}

当我运行此查询时:

session.createQuery("select i from InvoiceItem i").list();

它执行一个查询以从 InvoiceItem 中选择记录,如果我有 10000 个发票项目,它会生成 10000 个附加查询以从每个 InvoiceItem 中选择发票。

我认为如果可以在单个 sql 中获取所有记录会更好。实际上,我觉得很奇怪为什么它不是默认行为。

那么,我该怎么做呢?

4

4 回答 4

6

这里的问题与 Hibernate 无关,而与 JPA 有关。

在 JPA 1.0 之前,Hibernate 3 对所有关联使用延迟加载。

但是,JPA 1.0 规范FetchType.LAZY仅用于集合关联:

和关联默认使用@ManyToOne,从性能角度来看这是非常糟糕的。@OneToOneFetchType.EAGER

此处描述的行为称为 [N+1 查询问题][5],它发生是因为 Hibernate 需要确保@ManyToOne在将结果返回给用户之前初始化关联。

现在,如果您使用通过 直接获取entityManager.find,Hibernate 可以使用 LEFT JOIN 来初始化FetchTYpe.EAGER关联。

但是,当执行一个没有显式使用 JOIN FETCH 子句的查询时,Hibernate 将不会使用 JOIN 来获取FetchTYpe.EAGER关联,因为它无法更改您已经指定如何构造的查询。因此,它只能使用辅助查询。

修复很简单。仅FetchType.LAZY用于所有关联:

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "invoice_number", insertable = false, updatable = false)
   private Invoice invoice;

此外,您应该使用db-util 项目来断言 JPA 和 Hibernate 执行的语句数。

于 2019-02-05T06:19:50.740 回答
2

尝试

session.createQuery("select i from InvoiceItem i join fetch i.invoice inv").list();

它应该使用连接在单个 SQL 查询中获取所有数据。

于 2015-01-06T13:20:07.403 回答
1

是的,有您需要的设置:@BatchSize(size=25). 在这里检查:

20.1.5。使用批量获取

小引用:

使用批量获取,如果访问一个代理,Hibernate 可以加载多个未初始化的代理。批量抓取是对惰性选择抓取策略的优化。有两种方法可以配置批量获取:在类级别和集合级别。

类/实体的批量获取更容易理解。考虑以下示例:在运行时,您在 Session 中加载了 25 个 Cat 实例,每个 Cat 都有对其所有者的引用,即 Person。Person 类使用代理lazy="true" 进行映射。如果您现在遍历所有猫并在每个猫上调用 getOwner(),默认情况下,Hibernate 将执行 25 个 SELECT 语句来检索代理的所有者。您可以通过在 Person 的映射中指定批量大小来调整此行为:

<class name="Person" batch-size="10">...</class>

指定这个批大小后,Hibernate 现在将在需要访问未初始化的代理时按需执行查询,如上,但不同的是,它不会查询正在访问的确切代理实体,而是一次查询更多人的所有者,因此,当访问其他人的所有者时,它可能已经被此批量提取初始化,只有少数(远少于 25 个)查询将被执行。

因此,我们可以在两者上使用该注释:

  • 集合/集
  • 类/实体

也在这里检查:

于 2015-01-06T13:35:19.870 回答
0

在此方法中,触发了多个 SQL。第一个被触发以检索父表中的所有记录。其余的被触发以检索每个父记录的记录。第一个查询从数据库中检索 M 条记录,在本例中为 M 条父记录。对于每个父项,一个新查询检索子项。

于 2015-01-06T13:19:51.497 回答