我在保存附加了子对象的实体时遇到问题。以下代码在尝试保存Ad实体时抛出“org.hibernate.PersistentObjectException:传递给persist的分离实体:nl.test.api.domain.attribute.Attribute”,知道它的子对象字段中有Cascade.ALL 。重要的是,我将IdClass用于复合主键,并将对象Ad和Attribute用作AdAttribute的复合主键的一部分。
我理解异常意味着什么,但无论哪种方式我都无法修复它,特别是因为创建方法是Transactional。有什么想法吗?
根/广告对象:
@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;
(...)
}
“子对象”(包含一个复合键,其中@ManyToOne对象是键的一部分)
@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());
}
最后,我创建父对象(Ad)并附加子对象(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;
}
答案 0 :(得分:0)
引发PersistentObjectException ...detached entity passed to persist...
,因为Attribute
不是transient实例,而AFAIK,你的二级缓存在这里无关!
使用Attribute
关联,直接或通过其他id
检查您是否将Entity
实体与指定的CascadeType.PERSIST/ALL
保持一致
要解决此问题,您可以使用EntityManager.merge,但最好检查一下您的持久性和级联策略,以便更好地控制数据流,并且永远不要公开修改主数据的方法key(如果你没有使用自动生成的策略,则在构造函数中除外)。
您可以检查此SO thread中的其他可能解决方案,以便例外!
注意:
在CompositeAdAttributePk.equals
使用instanceOf
代替getClass()
,因为hibernate可能会使用代理+,您也应该检查null id
。
...如果您使用的是自动生成的身份,请避免在id
方法中使用surrogate equals/hasCode
,因为它在所有实体状态中都不是常量,而是使用业务密钥,我建议你阅读this有关该主题的文章。
希望它有所帮助!