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

beg*_*er_ 31 java merge persist spring-data spring-data-jpa

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

JPA级联持久化并且对分离实体的引用会抛出PersistentObjectException.为什么?

我正在创建一个引用现有的独立实体的新实体.现在,当我在Spring数据存储库中保存此实体时,会抛出异常:

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist
Run Code Online (Sandbox Code Playgroud)

如果我们看一下spring数据JPA的源代码中的save()方法,我们看到:

public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我们看看isNew()in AbstractEntityInformation

public boolean isNew(T entity) {

    return getId(entity) == null;
}
Run Code Online (Sandbox Code Playgroud)

所以基本上如果我保存()一个新实体(id == null),spring数据将始终调用persist,因此这种情况总是会失败.

在向集合添加新项目时,这似乎是一个非常典型的用例.

我该如何解决这个问题?

编辑1:

注意:

此问题与如何保存引用Spring JPA中现有实体的新实体没有直接关系.详细说明假设您获得了通过http创建新实体的请求.然后,您从请求中提取信息,并创建您的实体和现有的实体.因此,他们将永远脱离.

Jon*_*gat 12

我有一个类似的问题,我试图用一个已保存的实体对象保存一个新的实体对象.

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

public class MyEntity implements Persistable<Long> {

    public boolean isNew() {
        return null == getId() &&
            subEntity.getId() == null;
    }
Run Code Online (Sandbox Code Playgroud)

或者您可以使用AbstractPersistable并覆盖那里的isNew.

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

  • 如果 subEntity 恰好是带有 @OneToMany 映射的 List&lt;...&gt; 怎么办 (2认同)

beg*_*er_ 9

我想出的最好的是

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; 
}
Run Code Online (Sandbox Code Playgroud)

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

这要求新实体的服务保存对引用实体的服务的引用.这应该不是问题,因为您仍然使用spring,以便可以将服务添加为新@Autowired字段.

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

重要的提示:

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

@PersistenceContext
private EntityManager entityManager;
Run Code Online (Sandbox Code Playgroud)

并在上面的if(){}块使用

containable = entityManager.merge(containable);
Run Code Online (Sandbox Code Playgroud)

而不是我的代码(如果它工作未经测试).

在我的情况下,类是抽象的,targetEntity@ManyToOne是抽象的,因此也.直接调用entityManager.merge(可包含)然后会导致异常.但是,如果你的课程都是具体的,这应该有效.