I'm having problems in saving an entity that has child objects attached. The following code is throwing "org.hibernate.PersistentObjectException: detached entity passed to persist: nl.test.api.domain.attribute.Attribute" when trying to save the Ad entity, knowing that it has Cascade.ALL in its child object field. Important to say that I'm using IdClass for composite primary key and using objects Ad and Attribute as parts of the composite primary key of AdAttribute.
I understand what the exception means but either way I'm not able to fix it, specially because the creation method is Transactional. Any thoughts?
The root/Ad object:
@Entity
@DynamicInsert
@DynamicUpdate
@Table(name = "ad")
public class Ad implements SearchableAdDefinition {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, optional = false)
private User user;
@OneToMany(mappedBy = "ad", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<AdAttribute> adAttributes;
(...)
}
The "Child object" (which contains a composite key in which the @ManyToOne objects are part of the key)
@Entity
@Table(name = "attrib_ad")
@IdClass(CompositeAdAttributePk.class)
public class AdAttribute {
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ad_id")
private Ad ad;
@Id
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "attrib_id")
private Attribute attribute;
@Column(name = "value", length = 75)
private String value;
public Ad getAd() {
return ad;
}
public void setAd(Ad ad) {
this.ad = ad;
}
public Attribute getAttribute() {
return attribute;
}
public void setAttribute(Attribute attribute) {
this.attribute = attribute;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
class CompositeAdAttributePk implements Serializable {
private Ad ad;
private Attribute attribute;
public CompositeAdAttributePk() {
}
public CompositeAdAttributePk(Ad ad, Attribute attribute) {
this.ad = ad;
this.attribute = attribute;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompositeAdAttributePk compositeAdAttributePk = (CompositeAdAttributePk) o;
return ad.getId().equals(compositeAdAttributePk.ad.getId()) && attribute.getId().equals(compositeAdAttributePk.attribute.getId());
}
@Override
public int hashCode() {
return Objects.hash(ad.getId(), attribute.getId());
}
And finally, the method I create the parent object (Ad) and attach the child objects (Ad Attributes):
@Transactional
public Ad create(String title, User user, Category category, AdStatus status, String description, String url, Double price, AdPriceType priceType, Integer photoCount, Double minimumBid, Integer options, Importer importer, Set<AdAttribute> adAttributes) {
Ad ad = new Ad();
ad.setTitle(title);
ad.setUser(user);
ad.setCategory(category);
ad.setStatus(status);
ad.setDescription(description);
ad.setUrl(url);
ad.setPrice(price);
ad.setPriceType(priceType);
ad.setPhotoCount(photoCount);
ad.setMinimumBid(minimumBid);
ad.setOptions(options);
ad.setImporter(importer);
ad.setAdAttributes(adAttributes);
for (AdAttribute adAttribute : ad.getAdAttributes()) {
adAttribute.setAd(ad);
}
ad = adRepository.save(ad);
solrAdDocumentRepository.save(AdDocument.adDocumentBuilder(ad));
return ad;
}