我在保存附加了子对象的实体时遇到了问题。以下代码在尝试保存Ad实体时抛出“org. hibernate.PerstientObjectException:传递给持久化的分离实体:nl.test.api.domain.属性。属性”,因为知道它的子对象字段中有Cascade.ALL。重要的是,我使用IdClass作为复合主键,并使用对象Ad和属性作为AdAt的复合主键的一部分。
我理解异常的含义,但无论哪种方式我都无法修复它,特别是因为创建方法是事务性的。有什么想法吗?
根/广告对象:
@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属性)的方法:
@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;
}
被抛出是因为属性
不是瞬态实例,AFAIK,您的二级缓存与此无关!
检查您是否使用CascadeType. PERSIST/ALL
关联直接或通过其他Entity
使用分配的id
实体持久化属性
实体,
要解决这个问题,您可以使用EntityManager. merge,但是最好检查您的持久性和级联策略以更好地控制您的数据流,并且永远不要公开修改主键的方法(除非在配置器中,如果您不使用自动生成的策略)。
您也可以在此SO线程中检查其他可能的解决方案以及您的异常!
注:
在CompositeAdAtbantePk. equals
中使用instanceOf
而不是getClass()
,因为Hibernate可能会使用代理,您也应该检查nullid
。
…此外,如果您使用自动生成的标识,请避免在equals/hasCode
方法中使用surrogateid
,因为它在所有实体状态中都不是恒定的,请使用业务密钥,我建议您阅读有关在Hibernate中实现Equals和HashCode的信息,因为它指出:
…与其使用数据库标识符进行相等比较,你应该使用一组用于equals()的属性来标识你的各个对象。例如,如果你有一个“Item”类,它有一个“name”String和“创建”Date,我可以同时使用两者来实现一个好的equals()方法。不需要使用持久标识符,所谓的“业务键”要好得多。它是一个自然键,但这次使用它没有错!
希望有帮助!