Spring Boot Hibernate 多对多关系。传递给持久化的脱离实体:

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

Spring boot Hibernate Many to Many Relationship. Detached entity passed to persist:

问题

以下是您提供的代码的翻译部分:

我有两个实体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 productOption1 = new ProductOption("带Cocktailsauce", new BigDecimal(0), null, null, null, null);
    
    ProductOption productOption2 = new ProductOption("带Joghurtsauce", new BigDecimal(0), null, null, null, null);
    ProductOption productOption3 = new ProductOption("不带Sauce", new BigDecimal(0), null, null, null, null);

    Product product37 = new Product("Falafel", ProductCategory.Vegatarische_Döner, "选择:带Cocktailsauce,带Joghurtsauce或不带Sauce。", new BigDecimal(5.00));

    product37.getProductOptions().add(productOption1);
    product37.getProductOptions().add(productOption2);
    product37.getProductOptions().add(productOption3);
    productOption1.getProduct().add(product37);
    productOption2.getProduct().add(product37);
    productOption3.getProduct().add(product37);

    Product product38 = new Product("Falafel Yufka Dürüm", ProductCategory.Vegatarische_Döner, "选择:带Cocktailsauce,带Joghurtsauce或不带Sauce。", new BigDecimal(5.50));
    product38.getProductOptions().add(productOption1);
    product38.getProductOptions().add(productOption2);
    product38.getProductOptions().add(productOption3);
    productOption1.getProduct().add(product38);
    productOption2.getProduct().add(product38);
    productOption3.getProduct().add(product38);

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

它给我提供了以下异常

由于:org.hibernate.PersistentObjectException: 传递到持久化的脱离实体: 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]

但如果我不持久化product38

this.productRepository.save(product38);

那么我就没有任何问题。看起来我不能多次持久化相同的实例,因为productOption1-3已经与product37一起持久化了?

我是否必须每次创建新实例,尽管内容相同?这里是否有任何解决方法?

请提供建议。谢谢。

更新
忘了提到Equals在两个实体中都实现了。

@Override

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;
// 省略其他比较...
return true;
}

首先保存ProductOptions并重复使用它们也没有帮助。

private void initProducts() {
ProductOption productOption1 = new ProductOption("带Cocktailsauce", new BigDecimal(0), null, null, null, null);
ProductOption productOption2 = new ProductOption("带Joghurtsauce", new BigDecimal(0), null, null, null, null);
ProductOption productOption3 = new ProductOption("不带Sauce", new BigDecimal(0), null, null, null, null);

Product product37 = new Product("Falafel", ProductCategory.Vegatarische_Döner, "选择:带Cocktailsauce,带Joghurtsauce或不带Sauce。", new BigDecimal(5.00));
product37.getProductOptions().add(productOption1);
product37.getProductOptions().add(productOption2);
product37.getProductOptions().add(productOption3);
productOption1.getProduct().add(product37);
productOption2.getProduct().add(product37);
productOption3.getProduct().add(product37);
Product product38 = new Product("Falafel Yufka Dürüm", ProductCategory.Vegatarische_Döner, "选择:带Cocktailsauce,带Joghurtsauce或不带Sauce。", new BigDecimal(5.50));
product38.getProductOptions().add(productOption1);
product38.getProductOptions().add(productOption2);
product38.getProductOptions().add(productOption3);
productOption1.getProduct().add(product38);
productOption2.getProduct().add(product38);
productOption3.getProduct().add(product38);
this.productOptionRepository.save(productOption1);
this.productOptionRepository.save(productOption2);
this.productOptionRepository.save(productOption3);
this.productRepository.save(product37);
this.productRepository.save(product38);
英文:

I have 2 entities Product and ProductOptions with manytomany Relationship.

@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=&quot;product_productoption&quot;,joinColumns=@JoinColumn(name=&quot;Product_id&quot;), inverseJoinColumns=@JoinColumn(name=&quot;ProductOption_id&quot;))
private Set&lt;ProductOption&gt;productOptions=new HashSet&lt;&gt;();
}

And

@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 = &quot;productOptions&quot;)
private Set&lt;Product&gt;product=new HashSet&lt;&gt;();
}

Data initialisation

private void initProducts() {
ProductOption productOpton1=new ProductOption(&quot;mit Cocktailsauce&quot;, new BigDecimal(0), null, null, null, null);
ProductOption productOpton2=new ProductOption(&quot;mit Joghurtsauce&quot;, new BigDecimal(0), null, null, null, null);
ProductOption productOpton3=new ProductOption(&quot;ohne Sauce&quot;, new BigDecimal(0), null, null, null, null);
Product product37= new Product(&quot;Falafel&quot;, ProductCategory.Vegatarische_D&#246;ner, &quot;Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.&quot;,  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(&quot;Falafel Yufka D&#252;r&#252;m&quot;, ProductCategory.Vegatarische_D&#246;ner, &quot;Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.&quot;,  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);		
}

it gives me following exception

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]

but if i do not persist the product38

this.productRepository.save(product38);	

then i do not have any problem . it seems like i cannot persist the same instance multiple times ?since productOption 1-3 are already persisted with product37 ?

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

Do i have to create new instance everytime though content is same . Is there any workaround here ?

Please advice. Thanks.

**Update **
forget to mention that
Equals is implemented in both the entities.

@Override

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

Saving ProductOptions first and reusing them does not help either.

private void initProducts() {
ProductOption productOpton1=new ProductOption(&quot;mit Cocktailsauce&quot;, new BigDecimal(0), null, null, null, null);
ProductOption productOpton2=new ProductOption(&quot;mit Joghurtsauce&quot;, new BigDecimal(0), null, null, null, null);
ProductOption productOpton3=new ProductOption(&quot;ohne Sauce&quot;, new BigDecimal(0), null, null, null, null);
Product product37= new Product(&quot;Falafel&quot;, ProductCategory.Vegatarische_D&#246;ner, &quot;Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.&quot;,  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(&quot;Falafel Yufka D&#252;r&#252;m&quot;, ProductCategory.Vegatarische_D&#246;ner, &quot;Wahl aus: mit Cocktailsauce, mit Joghurtsauce oder ohne Sauce.&quot;,  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);
}

答案1

得分: 1

你需要在initProducts方法上加上@Transactional注解,如果你希望在一个方法中保存多个不同的数据。问题似乎是,在你想要保存product38时,ProductOptions还没有保存到数据库中。使用@Transactional,Spring会创建一个事务,所有操作将会被执行。

英文:

You need @Transactional at the method initProducts if you want to save multiple different data in one method.
The problem seems to be, that to the moment that you want to save product38 the ProductOptions not saved in DB yet. With @Transactional spring create a transaction and all operations will be performed.

huangapple
  • 本文由 发表于 2020年1月6日 20:42:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/59612336.html
匿名

发表评论

匿名网友

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

确定