在将Composite Key与Objects一起使用时,传递的分离实体将保持不变

时间:2017-11-06 21:26:18

标签: java spring hibernate jpa spring-boot

我在保存附加了子对象的实体时遇到问题。以下代码在尝试保存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;
}

1 个答案:

答案 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有关该主题的文章。

希望它有所帮助!