Is Hibernate supposed to send SELECT for children collection of fetched parent while being marked with FetchType.Lazy?

huangapple go评论66阅读模式
英文:

Is Hibernate supposed to send SELECT for children collection of fetched parent while being marked with FetchType.Lazy?

问题

我正在尝试理解Hibernate中的FetchType.Lazy是如何工作的,我感到有些困惑...

我有一个类似以下的Order实体:

@Entity
@Table(name = "customer_order")
public class Order {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    private Cart cart;
    // 单向关联

    ...其他部分为了简洁起见省略...
}

以及Cart实体:

@Entity
@Table(name = "cart")
public class Cart {

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

     ...为了简洁起见省略一些内容...

    @JsonManagedReference
    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<OrderItem> orderItems = new ArrayList<>();

    public void addItem(OrderItem item) {
        orderItems.add(item);
        item.setCart(this);
    }

    public void removeItem(OrderItem item) {
        orderItems.remove(item);
        item.setCart(null);
    }
}

最后是OrderItem:

@Entity
@Table(name = "order_item")
public class OrderItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    ...为了简洁起见省略一些字段...

    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    private Cart cart;

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (!(obj instanceof OrderItem))
            return false;

        return id != null && id.equals(((OrderItem) obj).getId());
    }
}

我有一个控制器 -> 服务实现 -> 存储库实现,我像这样获取一个订单:

@Override
public Order findById(Long id) {
    System.out.println(em.find(Order.class, id)); // 用于检查内存中获取了什么
    return em.find(Order.class, id);
}

Hibernate生成了以下查询:

Hibernate: 
select
    o1_0.id,
    a1_0.id,
    a1_0.door,
    a1_0.floor,
    a1_0.gate,
    a1_0.staircase,
    a1_0.street,
    a1_0.street_nr,
    c1_0.id,
    c1_0.total_cost,
    c1_0.total_cost_offers,
    c1_0.total_quantity,
    o1_0.contact_tel,
    o1_0.created_on,
    o1_0.c_first_name,
    o1_0.c_last_name,
    e1_0.id,
    e1_0.email,
    o2_0.id,
    o2_0.change_requested,
    o2_0.delivery_comment,
    o2_0.delivery_hour,
    o2_0.payment_change,
    o2_0.payment_type,
    o1_0.updated_on,
    o1_0.user_id 
from
    customer_order o1_0 
left join
    address a1_0 
        on a1_0.id=o1_0.address_id 
left join
    cart c1_0 
        on c1_0.id=o1_0.cart_id 
left join
    email e1_0 
        on e1_0.id=o1_0.email_id 
left join
    order_details o2_0 
        on o2_0.id=o1_0.order_details_id 
where
    o1_0.id=?

这是Cart对象在Postman中的输出:

"cart": {
    "id": 3,
    "totalQuantity": 2,
    "totalCost": 29.5,
    "totalCostOffers": null,
    "orderItems": [
        {
            "id": 5,
            "productType": "pizza",
            "name": "Carbonara",
            "format": "M",
            "quantity": 1,
            "price": 14.75
        },
        {
            "id": 6,
            "productType": "pizza",
            "name": "Trufa Gurmet",
            "format": "M",
            "quantity": 1,
            "price": 14.75
        }
    ]
},

根据我对FetchType.LAZY的理解,只有在我执行以下操作时,OrderItems的SELECT才应该发生:

order.getCart().getOrderItems()

因为OneToMany默认的FetchType是LAZY。直到那时,JSON输出应该是这样的:

"cart": {
    "id": 3,
    "totalQuantity": 2,
    "totalCost": 29.5,
    "totalCostOffers": null
}

如果购物车有很多订单项子项,如何实现它们的"延迟"加载,以便GET请求高效/不重?然后只在需要时加载购物车项目(例如,使用另一个GET)。或者我描述的功能与DTO投影有关,而不是FetchType.LAZY,我是否混淆了两者?

谢谢!

英文:

I'm trying to understand how FetchType.Lazy works with Hibernate and I'm scratching my head...

I have an Order entity like so:

@Entity
@Table(name = &quot;customer_order&quot;)
public class Order {

@Id
@Column(name = &quot;id&quot;)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne(cascade = CascadeType.ALL)
private Cart cart;
// unidirectional association

... other stuff omitted for brevity ...

And Cart Entity:

@Entity
@Table(name = &quot;cart&quot;)
public class Cart {

@Id
@Column(name = &quot;id&quot;)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 ... some stuff omitted for brevity ...

@JsonManagedReference
@OneToMany(mappedBy = &quot;cart&quot;, cascade = CascadeType.ALL, orphanRemoval = true)
private List&lt;OrderItem&gt; orderItems = new ArrayList&lt;&gt;();

public void addItem(OrderItem item) {
	orderItems.add(item);
	item.setCart(this);
}

public void removeItem(OrderItem item) {
	orderItems.remove(item);
	item.setCart(null);
}

And finally OrderItem:

Entity
@Table(name = &quot;order_item&quot;)
public class OrderItem {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = &quot;id&quot;)
private Long id;

... fields omitted for brevity ...

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
private Cart cart;

@Override
public int hashCode() {
	return getClass().hashCode();
}

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;

	if (!(obj instanceof OrderItem))
		return false;

	return id != null &amp;&amp; id.equals(((OrderItem) obj).getId());
}

I have a controller -> serviceimpl -> repositoryimpl and I fetch an order like so:

@Override
public Order findById(Long id) {
	System.out.println(em.find(Order.class, id)); // to check what&#39;s fetched in memory
	return em.find(Order.class, id);
}

Hibernate generates the following:

Hibernate: 
select
    o1_0.id,
    a1_0.id,
    a1_0.door,
    a1_0.floor,
    a1_0.gate,
    a1_0.staircase,
    a1_0.street,
    a1_0.street_nr,
    c1_0.id,
    c1_0.total_cost,
    c1_0.total_cost_offers,
    c1_0.total_quantity,
    o1_0.contact_tel,
    o1_0.created_on,
    o1_0.c_first_name,
    o1_0.c_last_name,
    e1_0.id,
    e1_0.email,
    o2_0.id,
    o2_0.change_requested,
    o2_0.delivery_comment,
    o2_0.delivery_hour,
    o2_0.payment_change,
    o2_0.payment_type,
    o1_0.updated_on,
    o1_0.user_id 
from
    customer_order o1_0 
left join
    address a1_0 
        on a1_0.id=o1_0.address_id 
left join
    cart c1_0 
        on c1_0.id=o1_0.cart_id 
left join
    email e1_0 
        on e1_0.id=o1_0.email_id 
left join
    order_details o2_0 
        on o2_0.id=o1_0.order_details_id 
where
    o1_0.id=?
--- Should this following SELECT happen? Is it intended behavior since OneToMany default fetchType is LAZY?
Hibernate: 
select
    o1_0.cart_id,
    o1_0.id,
    o1_0.format,
    o1_0.name,
    o1_0.price,
    o1_0.product_type,
    o1_0.quantity 
from
    order_item o1_0 
where
    o1_0.cart_id=?

The output of cart obj in Postman is:

&quot;cart&quot;: {
    &quot;id&quot;: 3,
    &quot;totalQuantity&quot;: 2,
    &quot;totalCost&quot;: 29.5,
    &quot;totalCostOffers&quot;: null,
    &quot;orderItems&quot;: [
        {
            &quot;id&quot;: 5,
            &quot;productType&quot;: &quot;pizza&quot;,
            &quot;name&quot;: &quot;Carbonara&quot;,
            &quot;format&quot;: &quot;M&quot;,
            &quot;quantity&quot;: 1,
            &quot;price&quot;: 14.75
        },
        {
            &quot;id&quot;: 6,
            &quot;productType&quot;: &quot;pizza&quot;,
            &quot;name&quot;: &quot;Trufa Gurmet&quot;,
            &quot;format&quot;: &quot;M&quot;,
            &quot;quantity&quot;: 1,
            &quot;price&quot;: 14.75
        }
    ]
},

By the understanding I have of FetchType.LAZY, shouldn't the SELECT of OrderItems only happen if I do:

order.getCart().getOrderItems()

because OneToMany is FetchType.LAZY by default?

And until then the JSON ouput should be:

&quot;cart&quot;: {
    &quot;id&quot;: 3,
    &quot;totalQuantity&quot;: 2,
    &quot;totalCost&quot;: 29.5,
    &quot;totalCostOffers&quot;: null,
},

If a cart would have a lot of order items children, how can they be "lazy" loaded/fetched so that the GET request would be efficient/not heavy? And then only load the cart items on demand (with another GET for example). Or the functionality I'm describing has to do with DTO projections and not FetchType.LAZY and I'm confusing the two?

Thanks!

答案1

得分: 0

在查看之前提出的相关问题列表时,我找到了一个类似但不完全相同的情况,这让我找到了额外的SELECT语句的罪魁祸首:

在Cart Entity中,我在自动生成的toString()方法中包括了orderItems

@Override
public String toString() {
    return "Cart [id=" + id + ", totalQuantity=" + totalQuantity + ", totalCost=" + totalCost +
            ", totalCostOffers=" + totalCostOffers + ", orderItems=" + orderItems + "]";
}

我移除了+ ", orderItems=" + orderItems,现在Hibernate生成了:

Hibernate: 
select
    o1_0.id,
    a1_0.id,
    a1_0.door,
    a1_0.floor,
    a1_0.gate,
    a1_0.staircase,
    a1_0.street,
    a1_0.street_nr,
    c1_0.id,
    c1_0.total_cost,
    c1_0.total_cost_offers,
    c1_0.total_quantity,
    o1_0.contact_tel,
    o1_0.created_on,
    o1_0.c_first_name,
    o1_0.c_last_name,
    e1_0.id,
    e1_0.email,
    o2_0.id,
    o2_0.change_requested,
    o2_0.delivery_comment,
    o2_0.delivery_hour,
    o2_0.payment_change,
    o2_0.payment_type,
    o1_0.updated_on,
    o1_0.user_id 
from
    customer_order o1_0 
left join
    address a1_0 
        on a1_0.id=o1_0.address_id 
join
    cart c1_0 
        on c1_0.id=o1_0.cart_id 
left join
    email e1_0 
        on e1_0.id=o1_0.email_id 
left join
    order_details o2_0 
        on o2_0.id=o1_0.order_details_id 
where
    o1_0.id=?

JSON输出如下:

"cart": {
    "id": 3,
    "totalQuantity": 2,
    "totalCost": 29.5,
    "totalCostOffers": null,
    "orderItems": null
},

我仍然发布了问题,现在我正在添加答案,希望对其他人有所帮助!

原始帮助我的答案是:

https://stackoverflow.com/questions/32723627/when-query-to-parent-child-also-being-fetched-hibernate

英文:

So looking through the list of previously related questions asked I found something similar, but not quite the same thing that lead me to find the culprit of the additional SELECT statement:

In Cart Entity I had the auto-generated toString() include orderItems

    @Override
public String toString() {
return &quot;Cart [id=&quot; + id + &quot;, totalQuantity=&quot; + totalQuantity + &quot;, totalCost=&quot; + totalCost +     &quot;, totalCostOffers=&quot;
			+ totalCostOffers + &quot;, orderItems=&quot; + orderItems + &quot;]&quot;;
}

I removed + ", orderItems=" + orderItems and now Hibernate generates:

Hibernate: 
select
    o1_0.id,
    a1_0.id,
    a1_0.door,
    a1_0.floor,
    a1_0.gate,
    a1_0.staircase,
    a1_0.street,
    a1_0.street_nr,
    c1_0.id,
    c1_0.total_cost,
    c1_0.total_cost_offers,
    c1_0.total_quantity,
    o1_0.contact_tel,
    o1_0.created_on,
    o1_0.c_first_name,
    o1_0.c_last_name,
    e1_0.id,
    e1_0.email,
    o2_0.id,
    o2_0.change_requested,
    o2_0.delivery_comment,
    o2_0.delivery_hour,
    o2_0.payment_change,
    o2_0.payment_type,
    o1_0.updated_on,
    o1_0.user_id 
from
    customer_order o1_0 
left join
    address a1_0 
        on a1_0.id=o1_0.address_id 
join
    cart c1_0 
        on c1_0.id=o1_0.cart_id 
left join
    email e1_0 
        on e1_0.id=o1_0.email_id 
left join
    order_details o2_0 
        on o2_0.id=o1_0.order_details_id 
where
    o1_0.id=?

and the JSON output is:

&quot;cart&quot;: {
    &quot;id&quot;: 3,
    &quot;totalQuantity&quot;: 2,
    &quot;totalCost&quot;: 29.5,
    &quot;totalCostOffers&quot;: null,
    &quot;orderItems&quot;: null
},

I still posted the question and now I'm adding the answer so maybe it's helpful to others!

Original answer that helped me was:

https://stackoverflow.com/questions/32723627/when-query-to-parent-child-also-being-fetched-hibernate

huangapple
  • 本文由 发表于 2023年5月28日 01:15:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76348105.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定