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

问题描述 投票:5回答:4

我有这些课程:

@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中获取,那会更好。实际上,我觉得为什么它不是默认行为很奇怪。

所以,我该怎么办?

java hibernate jpa many-to-one select-n-plus-1
4个回答
2
投票

尝试使用

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

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


6
投票

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

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

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

@ManyToMany@ElementCollection关联默认使用@ElementCollection,即@ManyToOne

这里描述的行为称为@ManyToOne,它的发生是因为Hibernate需要确保在将结果返回给用户之前初始化@OneToOne关联。

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

但是,当执行不显式使用JOIN FETCH子句的查询时,Hibernate将不使用JOIN来获取very bad from a performance perspective关联,因为它不能更改您已经指定了构造方式的查询。因此,它只能使用辅助查询。

修复很简单。只需对所有关联使用N+1 query issue

@ManyToOne

更多,您应该使用entityManager.find声明由JPA和Hibernate执行的语句数。有关更多详细信息,请查看FetchTYpe.EAGER


0
投票

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


0
投票

是,您需要进行以下设置:FetchTYpe.EAGER。在这里检查:

FetchType.LAZY

小引用:

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

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

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

指定了此批处理大小后,Hibernate现在将在需要访问未初始化的代理时按需执行查询,如上所述,但是区别在于,它不是查询所访问的确切代理实体,而是查询更多Person的所有者。因此,一旦访问其他人的所有者,它可能已经通过此批访存进行了初始化,将仅执行少数(少于25个)查询。

因此,我们可以在两个注解上使用该注解:

  • 集合/集
  • 类/实体

也在这里检查:

© www.soinside.com 2019 - 2024. All rights reserved.