Spring boot休眠多对多关系。已传递到持久化的分离实体:

Spring boot休眠多对多关系。已传递到持久化的分离实体:,spring,hibernate,spring-boot,jpa,spring-data-jpa,Spring,Hibernate,Spring Boot,Jpa,Spring Data Jpa,我有两个实体产品和产品选项与许多关系 @Entity public class Product { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; private String description; private String productCategory; private String optionDescription; private Bi

我有两个实体产品和产品选项与许多关系

@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<>();
}
它给了我以下的例外

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]
但是如果我不坚持下去

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);




}

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

您是否实现了equals()?@SimonMartinelli equals确实实现了。您应该先保存生产选项,然后将thme分配给product@SimonMartinelli这也无济于事。请查看我文章的最后一部分。我已经更新了代码。您必须在事务中运行initProducts
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;
}
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);




}