Spring Data JPA 中的 OneToMany 双向关系 JoinColumn 值为 null

Jad*_*dda 1 spring-data spring-data-jpa spring-boot

我有两个实体 Cart 和 CartProduct 的 OneToMany 双向映射。每当我们插入带有购物车产品的 Cart 对象时,CartProduct 表应填充cart_id。问题是,当我插入购物车对象时,一切似乎都很好,除了JoinColumn(card_id)这会导致 CartProduct 表中出现空值。我这样做对吗?

购物车Java

package com.springtesting.model.cart;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.user.UserProfile;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@EqualsAndHashCode(callSuper = true)
@Entity
@Data
@Table(name = "cart")
public class Cart  extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 6294902210705780249L;

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

    @OneToOne
    @JoinColumn(name = "user_profile_id")
    @JsonIgnoreProperties(value = {"addresses"})
    private UserProfile userProfile;

    @ManyToOne
    @JoinColumn(name = "cart_status")
    private CartStatus cartStatus;

    @OneToMany(mappedBy = "cart", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
    //@ElementCollection(targetClass = CartProduct.class)
    private List<CartProduct> cartProducts=new ArrayList<>();

}
Run Code Online (Sandbox Code Playgroud)

购物车产品.Java

package com.springtesting.model.cart;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springtesting.model.AbstractAuditingEntity;
import com.springtesting.model.product.Product;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;

@EqualsAndHashCode(callSuper = true)
@Entity
@Data
@Table(name = "cart_product")
public class CartProduct extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 6498067041321289048L;

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

    @OneToOne
    @JoinColumn(name = "product_id")
    private Product product;

    @Column(name = "quantity")
    private Integer quantity;


    @ManyToOne
    @JoinColumn(name = "cart_id",referencedColumnName = "id")
    @JsonIgnoreProperties(value = {"userProfile","cartStatus","cartProducts"})
    private Cart cart;

}
Run Code Online (Sandbox Code Playgroud)

测试用例.java

 @Test
    public void insertCart()
    {
        Cart cart=new Cart();
        cart.setUserProfile(userProfileRepository.findAllByUserId(1L).get());
        cart.setCartStatus(cartStatusRepository.findById(1L).get());
        List<CartProduct> cartProducts=new ArrayList<>();

        CartProduct cartProduct=new CartProduct();
        cartProduct.setProduct(productRepository.findById(1L).get());
        cartProduct.setQuantity(2);
        cartProducts.add(cartProduct);

        cartProduct=new CartProduct();
        cartProduct.setProduct(productRepository.findById(2L).get());
        cartProduct.setQuantity(1);
        cartProducts.add(cartProduct);

        cart.setCartProducts(cartProducts);

        cartRepository.saveAndFlush(cart);

    }
Run Code Online (Sandbox Code Playgroud)

K.N*_*las 5

是的,您的解决方法是添加cartProduct.setCart(cart);“这是因为 CartProduct 是拥有实体并且是外键的保管者”。上述语句设置了FK。

思考这个问题的方法是概念owning entity。当你拥有这种关系时mappedBy="cart",你就说该类CartProduct拥有这种关系。这意味着只有CartProduct类在进行持久化。这告诉 JPA 在表中创建一个 FK CartProduct。然而,我们注意到 save 并没有被调用CartProduct,而是 onCart并且cartProducts仍然被保存。这是因为你有cascade = CascadeType.ALL注释。这告诉 JPA 在完成某些操作Cart(在本例中为save操作)时级联某些操作。

您应该打印 SQL 语句并检查不同配置和测试用例的差异。这将帮助您更好地理解。

您还拥有 FetchType.EAGER。这通常是一个坏习惯,通常会导致无穷无尽的问题。

考虑双向映射的一个好方法是,它List<CartProducts> cartProducts是一个仅查询字段。为了保存a,CartProduct您可以直接调用save cartProductRepository。例如

CartProduct cartProduct=new CartProduct();
cartProduct.setProduct(productRepository.findById(1L).get());
cartProduct.setQuantity(2);
cartProduct.setCart(cart);
cartProductRepository.save(cartProduct);
Run Code Online (Sandbox Code Playgroud)

进而

cart.getCartProducts().add(cartProduct);
Run Code Online (Sandbox Code Playgroud)

并删除所有级联和急切获取注释。当 hibernate 说你必须管理关系的双方时,这就是这个意思。

这样做将导致一次保存查询。通过使用级联注释,您会发现,当您将商品添加到购物车并调用 save 时,生成的 sql 将首先cartProducts从数据库中删除所有现有商品,并在每次调用 save 时将它们与新商品一起重新添加。对于包含 10 个商品而不是单个保存的购物车,您将有一个删除和 10 个新保存。肯定不太理想。如果您必须从头开始重新加载购物车,最有效的方法是获取购物车,然后cart.setCartProducts(cartProductRepository.findAllByCart(cart));这就是 FetchType.EAGER 正在做的事情。当您了解这一切时,您就会意识到您不需要= new ArrayList<>();为您的cartProducts.