Spring-Data JPA:保存引用现有实体的新实体



问题与下面的问题基本相同:

JPA级联持久和对分离实体的引用抛出PersistentObjectException。为什么?

我正在创建一个新实体,它引用了一个现有的、分离的实体。现在,当我将这个实体保存到spring数据存储库中时,会抛出一个异常:

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist

如果我们查看spring data JPA源代码中的save()方法,我们会看到:

public <S extends T> S save(S entity) {
    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
如果我们看一下AbstractEntityInformation 中的isNew()
public boolean isNew(T entity) {
    return getId(entity) == null;
}

所以基本上如果我保存()一个新的实体(id == null), spring数据将总是调用持久化,因此这个场景将总是失败。

当向集合中添加新项时,这似乎是一个非常典型的用例。

如何解决这个问题?

编辑1:

注意:

这个问题不是直接关系到如何保存一个新的实体,引用现有的实体在Spring JPA?为了详细说明,假设您通过http获得创建新实体的请求。然后从请求中提取信息,并创建您的实体和现有的引用实体。因此,他们将永远是分离的。

我有一个类似的问题,我试图保存一个新的实体对象,里面已经保存了一个实体对象。

我所做的是实现Persistable<并相应地实现了isNew()。>

public class MyEntity implements Persistable<Long> {
    public boolean isNew() {
        return null == getId() &&
            subEntity.getId() == null;
    }

或者您可以使用AbstractPersistable并覆盖isNew。

我不知道这是否会被认为是处理这个问题的好方法,但它对我来说很好,而且感觉很自然。

我想到的最好的是

public final T save(T containable) {
    // if entity containable.getCompound already exists, it
    // must first be reattached to the entity manager or else
    // an exception will occur (issue in Spring Data JPA ->
    // save() method internal calls persists instead of merge)
    if (containable.getId() == null
            && containable.getCompound().getId() != null){
        Compound compound = getCompoundService()
                .getById(containable.getCompound().getId());
        containable.setCompound(compound);   
    }
    containable = getRepository().save(containable);
    return containable; 
}

我们检查我们是否处于有问题的情况,如果是,只需通过其id从数据库中重新加载现有实体,并将新实体的字段设置为这个新加载的实例。然后它将被附加。

这要求新实体的服务包含对被引用实体的服务的引用。这应该不是一个问题,因为你正在使用spring,所以服务可以作为一个新的@Autowired字段添加。

然而,另一个问题(在我的情况下,这种行为实际上是需要的),您不能在保存新实体的同时更改引用的现有实体。所有这些更改都将被忽略。

重要提示:

在许多情况下(可能是您的情况),这可以简单得多。您可以向您的服务添加实体管理器的引用:

@PersistenceContext
private EntityManager entityManager;

和以上if(){}块使用

containable = entityManager.merge(containable);

而不是我的代码(未测试是否有效)。

在我的情况下,类是抽象的,@ManyToOne中的targetEntity因此也是抽象的。直接调用entityManager.merge(containable)会导致异常。然而,如果你的类都是具体的,这应该工作。

我有同样的问题与@EmbeddedId和业务数据作为id的一部分。
知道实体是否是新实体的唯一方法是执行(em.find(entity)==null)?em.persist(entity):em.merge(entity)

但是spring-data只提供save()方法,没有办法用find()方法填充Persistable.isNew()方法

最新更新