首页 > 解决方案 > Spring boot Hibernate 多对多关系。传递给持久化的分离实体:

问题描述

我有 2 个具有多对多关系的实体 Product 和 ProductOptions。

@Entity
public class Product {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String description;
    private String productCategory;
    private String optionDescription;
    private BigDecimal productBasePrice;
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name="product_productoption",joinColumns=@JoinColumn(name="Product_id"), inverseJoinColumns=@JoinColumn(name="ProductOption_id"))
    private Set<ProductOption>productOptions=new HashSet<>();
}

@Entity
public class ProductOption {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String productOptionDescription;
    private BigDecimal optionPrice;
    private BigDecimal optionPriceForSmall;
    private BigDecimal optionPriceForNormal;
    private BigDecimal optionPriceForFamily;
    private BigDecimal optionPriceForParty;
    @ManyToMany(mappedBy = "productOptions")
    private Set<Product>product=new HashSet<>();
}

数据初始化

private void initProducts() {
    ProductOption productOpton1=new ProductOption("mit Cocktailsauce", new BigDecimal(0), null, null, null, null);

    ProductOption productOpton2=new ProductOption("mit Joghurtsauce", new BigDecimal(0), null, null, null, null);
    ProductOption productOpton3=new ProductOption("ohne Sauce", new BigDecimal(0), null, null, null, null);

    Product product37= new Product("Falafel", ProductCategory.Vegatarische_Döner, "Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.",  new BigDecimal(5.00));

    product37.getProductOptions().add(productOpton1);
    product37.getProductOptions().add(productOpton2);
    product37.getProductOptions().add(productOpton3);
    productOpton1.getProduct().add(product37);
    productOpton2.getProduct().add(product37);
    productOpton3.getProduct().add(product37);
    Product product38= new Product("Falafel Yufka Dürüm", ProductCategory.Vegatarische_Döner, "Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.",  new BigDecimal(5.50));
    product38.getProductOptions().add(productOpton1);
    product38.getProductOptions().add(productOpton2);
    product38.getProductOptions().add(productOpton3);
    productOpton1.getProduct().add(product38);
    productOpton2.getProduct().add(product38);
    productOpton3.getProduct().add(product38);



        this.productRepository.save(product37);
        this.productRepository.save(product38);     

}

它给了我以下异常

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: xx.xy.zz ProductOption
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final]
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final]
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:726) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:694) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final]
    at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298) ~[hibernate-core-5.4.9.Final.jar:5.4.9.Final]

但如果我不坚持产品38

this.productRepository.save(product38); 

那么我没有任何问题。似乎我不能多次保留同一个实例?因为 productOption 1-3 已经与 product37 一起保留?

product38.getProductOptions().add(productOpton1);
        product38.getProductOptions().add(productOpton2);
        product38.getProductOptions().add(productOpton3);

尽管内容相同,但我是否每次都必须创建新实例。这里有什么解决方法吗?

请指教。谢谢。

**更新**忘记提及 Equals 在两个实体中都实现了。

@覆盖

public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    ProductOption other = (ProductOption) obj;
    if (id == null) {
        if (other.id != null)
            return false;
    } else if (!id.equals(other.id))
        return false;
    if (optionPrice == null) {
        if (other.optionPrice != null)
            return false;
    } else if (!optionPrice.equals(other.optionPrice))
        return false;
    if (optionPriceForFamily == null) {
        if (other.optionPriceForFamily != null)
            return false;
    } else if (!optionPriceForFamily.equals(other.optionPriceForFamily))
        return false;
    if (optionPriceForNormal == null) {
        if (other.optionPriceForNormal != null)
            return false;
    } else if (!optionPriceForNormal.equals(other.optionPriceForNormal))
        return false;
    if (optionPriceForParty == null) {
        if (other.optionPriceForParty != null)
            return false;
    } else if (!optionPriceForParty.equals(other.optionPriceForParty))
        return false;
    if (optionPriceForSmall == null) {
        if (other.optionPriceForSmall != null)
            return false;
    } else if (!optionPriceForSmall.equals(other.optionPriceForSmall))
        return false;
    if (productOptionDescription == null) {
        if (other.productOptionDescription != null)
            return false;
    } else if (!productOptionDescription.equals(other.productOptionDescription))
        return false;
    return true;
}

首先保存 ProductOptions 并重用它们也无济于事。

private void initProducts() {
    ProductOption productOpton1=new ProductOption("mit Cocktailsauce", new BigDecimal(0), null, null, null, null);

    ProductOption productOpton2=new ProductOption("mit Joghurtsauce", new BigDecimal(0), null, null, null, null);
    ProductOption productOpton3=new ProductOption("ohne Sauce", new BigDecimal(0), null, null, null, null);

    Product product37= new Product("Falafel", ProductCategory.Vegatarische_Döner, "Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.",  new BigDecimal(5.00));

    product37.getProductOptions().add(productOpton1);
    product37.getProductOptions().add(productOpton2);
    product37.getProductOptions().add(productOpton3);
    productOpton1.getProduct().add(product37);
    productOpton2.getProduct().add(product37);
    productOpton3.getProduct().add(product37);
    Product product38= new Product("Falafel Yufka Dürüm", ProductCategory.Vegatarische_Döner, "Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.",  new BigDecimal(5.50));
    product38.getProductOptions().add(productOpton1);
    product38.getProductOptions().add(productOpton2);
    product38.getProductOptions().add(productOpton3);
    productOpton1.getProduct().add(product38);
    productOpton2.getProduct().add(product38);
    productOpton3.getProduct().add(product38);


    this.productOptionRepository.save(productOpton1);
    this.productOptionRepository.save(productOpton2);
    this.productOptionRepository.save(productOpton3);
        this.productRepository.save(product37);
        this.productRepository.save(product38);




}

标签: springhibernatespring-bootjpaspring-data-jpa

解决方案


如果要在一种方法中保存多个不同的数据,则需要在方法 initProducts 处使用 @Transactional。问题似乎是,在您想要保存 product38 的那一刻,ProductOptions 尚未保存在 DB 中。使用@Transactional spring 创建一个事务并执行所有操作。


推荐阅读