阻止 JPA/Hibernate 构造函数表达式"n+1 selects"?



我有两个实体,ItemData,以及一个DTO类ItemDataItemDataItemData组成,没有 JPA 映射。为了检索填充的 ItemData 列表,我在 JPQL 中使用了一个构造函数表达式:

select new my.package.ItemData(i, d)
from Item i, Data d
where i.id = d.itemId

这就是Hibernate正在做的事情:它不是同时获取ItemData的数据,而是首先获取它们的ID,然后在n个单独的选择语句中获取数据。有没有办法改变这种行为?

Hibernate:
    select
        item0_.id as col_0_0_,
        data1_.id as col_1_0_ 
    from
        ITEM item0_,
        DATA data1_
Hibernate: 
    select
        item0_.no as no1_0_,
        item0_.description as description1_0_,
        item0_.organic as bio1_0_,
        item0_.gluten as gluten1_0_,
        item0_.laktose as laktose1_0_
    from
        ITEM item0_ 
    where
        item0_.id=?
Hibernate: 
    select
        data0_.amount as amount1_3_0_,
        data0_.avg as avg3_0_,
        data0_.total as total3_0_
    from
        DATA data0_ 
    where
        data0_.id=?
Hibernate: 
    select
        item0_.no as no1_0_,
        item0_.description as description1_0_,
        item0_.organic as bio1_0_,
        item0_.gluten as gluten1_0_,
        item0_.laktose as laktose1_0_
    from
        ITEM item0_ 
    where
        item0_.id=?
Hibernate: 
    select
        data0_.amount as amount1_3_0_,
        data0_.avg as avg3_0_,
        data0_.total as total3_0_
    from
        DATA data0_ 
    where
        data0_.id=?
...and so on...        

使用左联接获取怎么样?
我承认我没有使用 CTOR 表达式,但是当我需要获取父实体及其子实体时,我使用了左联接获取,它总是像魅力一样工作。
我只能假设,既然我们在这里处理的是对象的构造,
Hibernate并不"想要"让对象"部分构造"(或者说 - 一个处于"惰性求值状态"的对象),并且由于您没有使用"左连接获取"执行"预先获取",因此它执行N + 1次获取:
首先,它获取所有 ID,然后为每个相关 ID 执行另一个提取。
在此处阅读有关左联接提取的更多信息(只需在页面上查找"左联接提取")

这是 Blaze-Persistence 实体视图的完美用例。

我创建了该库,以允许JPA模型与自定义接口或抽象类定义的模型之间的轻松映射,类似于类固醇上的Spring Data Projections。这个想法是你按照你喜欢的方式定义你的目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型。

我想你的实体模型看起来像这样?

@Entity
class Item {
  @Id Long id;
  String description;
  @OneToMany(mappedBy = "item")
  Data data;
  ...
}
@Entity
class Data {
  @Id Long id;
  @ManyToOne Item item;
  Long amount;
  BigDecimal avg;
  ...
}

您的用例的 DTO 模型可能如下所示,其中包含 Blaze-Persistence 实体视图:

@EntityView(Item.class)
public interface ItemData {
    @IdMapping
    Long getNo();
    String getDescription();
    @Mapping("SUM(data.amount)")
    Long getAmount();
}
查询

是将实体视图应用于查询的问题,最简单的只是按 id 进行查询。

ItemData a = entityViewManager.find(entityManager, ItemData.class, id);

Spring 数据

集成允许您像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

最新更新