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

mzillmmw  于 2023-01-13  发布在  其他
关注(0)|答案(4)|浏览(178)

我有这些课程:

@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中获取会好得多。实际上,我觉得奇怪的是为什么它不是默认行为。
那么,我该怎么做呢?

vd2z7a6w

vd2z7a6w1#

这里的问题与Hibernate无关,而是与JPA有关。
在JPA 1.0之前,Hibernate 3对所有关联使用延迟加载。
但是,JPA 1.0规范仅将FetchType.LAZY用于集合关联:

默认情况下,@ManyToOne@OneToOne关联使用FetchType.EAGER,从性能Angular 来看,这非常糟糕。
这里描述的行为被称为[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;

此外,应该使用Hypersistence Utils来AssertJPA和Hibernate执行的语句数。

bhmjp9jg

bhmjp9jg2#

试试

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

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

4xrmg8kj

4xrmg8kj3#

是的,有您需要的设置:@BatchSize(size=25)。请在此处查看:
20.1.5.使用批量取数

  • 小引用:*

使用批处理获取,Hibernate可以在访问一个代理时加载多个未初始化的代理。批处理获取是惰性选择获取策略的优化。有两种方法可以配置批处理获取:在类级别和集合级别上。
类/实体的批量获取更容易理解。考虑以下示例:在运行时,在一个会话中加载了25个Cat示例,并且每个Cat都有一个对其所有者Person的引用。Person类Map有一个代理,lazy=“true”。如果您现在遍历所有Cat并对每个Cat调用getOwner(),Hibernate将默认执行25个SELECT语句来检索代理的所有者。您可以通过在Person:

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

指定了这个批处理大小后,Hibernate现在将在需要访问未初始化的代理时按需执行查询,如上所述,但不同之处在于,它将一次查询更多Person的所有者,而不是查询正在访问的代理实体,因此,当访问其他人的所有者时,它可能已经被该批获取初始化,而将仅执行几个(远少于25个)查询。
因此,我们可以在以下两个方面使用该注解:

  • 集合/集
  • 类/实体

也可以在这里查看:

  • @BatchSize,但在@ManyToOne情况下有许多往返
gajydyqb

gajydyqb4#

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

相关问题