使用JPA / Hibernate构造函数表达式防止“n + 1选择”?

时间:2012-10-08 12:44:17

标签: java hibernate jpa select-n-plus-1

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

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

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

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...        

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

这是Blaze-Persistence Entity Views的完美用例。

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

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

@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 Entity-Views相似,如下所示:

@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 Data集成使您可以像使用Spring Data Projections一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features