Hibernate为@ManyToOne JPA注释属性创建N + 1个查询

时间:2015-01-06 13:13:52

标签: java hibernate jpa many-to-one select-n-plus-1

我有这些课程:

@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 个答案:

答案 0 :(得分:2)

尝试

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

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

答案 1 :(得分:1)

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

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

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

@ManyToOne@OneToOne关联默认使用FetchType.EAGER,即very bad from a performance perspective

此处描述的行为称为N+1 query issue,它的发生是因为Hibernate需要确保@ManyToOne关联已初始化,然后才能将结果返回给用户。

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

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

修复很简单。只需对所有关联使用FetchType.LAZY

@ManyToOne(获取= FetchType.LAZY)    @JoinColumn(名称=“发票编号”,可插入=假,可更新=假)    私人发票发票;

更多,您应该使用db-util project声明由JPA和Hibernate执行的语句数。有关更多详细信息,请查看this article

答案 2 :(得分:0)

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

20.1.5. Using batch fetching

小引用:

  

使用批量提取,如果访问一个代理,Hibernate可以加载几个未初始化的代理。批量提取是延迟选择提取策略的优化。您可以通过两种方式配置批量提取:在类级别和集合级别。

     

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

的映射中指定批量大小来调整此行为
<class name="Person" batch-size="10">...</class>
  

如果指定了这个批量大小,Hibernate现在将在需要访问未初始化代理时按需执行查询,如上所述,但区别在于它不会查询正在访问的确切代理实体,而是查询更多Person&# 39; s所有者,因此,当访问其他人的所有者时,它可能已经通过此批量提取初始化,只执行少量(少于25个)查询。

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

  • 集合/组
  • 类/实体

在这里查看:

答案 3 :(得分:-1)

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