2013-05-15 119 views
24

的问题基本上是相同的,如下一个:弹簧数据的JPA:保存新的实体引用现有

JPA cascade persist and references to detached entities throws PersistentObjectException. Why?

我创建引用现有的,分离的一个新的实体。现在,当我保存这个实体在我的春天数据存储库将抛出一个异常:

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

如果我们看一下在春天数据JPA的源代码保存()方法中,我们看到:

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

    if (entityInformation.isNew(entity)) { 
     em.persist(entity); 
     return entity; 
    } else { 
     return em.merge(entity); 
    } 
} 

和如果我们在AbstractEntityInformation

public boolean isNew(T entity) { 

    return getId(entity) == null; 
} 

看是否新款()所以基本上,如果我救()一个新的实体(id == null),春季数据将始终坚持打电话,因此该方案将ALW ays失败。

这似乎是向集合添加新项目时非常典型的用例。

我该如何解决这个问题?

编辑1:

注:

这个问题不直接关系到How to save a new entity that refers existing entity in Spring JPA?。详细说明假设你得到通过http创建新实体的请求。然后从请求中提取信息并创建您的实体和现有的实体。因此他们将永远被分离。

+1

卡住相同的情况..任何解决方案? – raksja

+0

不,不是真的......当然,您可以处理异常,然后先创建(保留)新对象,并在保留新对象后添加引用。但是,这并不适用于所有情况... –

+0

另一种选择是确定我们是否处于这种状态:'entity.getId()== null && entity.getReferencedEntity()。getId()!= null',如果为true从数据库加载引用的实体。 –

回答

8

我想出的最好的是

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数据库加载现有实体和新实体的字段设置为这个新加载的实例。然后它将被连接。

这要求新实体的服务持有对引用实体的服务的引用。这不应该成为一个问题,因为您仍然使用弹簧,以便可以将服务添加为新的@Autowired字段。

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

重要提示:

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

@PersistenceContext 
private EntityManager entityManager; 

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

containable = entityManager.merge(containable); 

,而不是我的代码(未经测试,如果它的工作原理)。

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

9

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

我做了什么实现Persistable < T>并相应地实现了isNew()。

public class MyEntity implements Persistable<Long> { 

    public boolean isNew() { 
     return null == getId() && 
      subEntity.getId() == null; 
    } 

或者你可以使用AbstractPersistable和覆盖是否新款有ofcourse。

我不知道这是否会被认为是处理这个问题的好方法,但对我来说它确实非常好,除此之外,感觉很自然。

+0

如果subEntity碰巧是List <...>和@OneToMany映射会怎么样 –

1

我有一个@EmbeddedId和业务数据作为ID的一部分相同的问题。
只有这样,才能知道,如果实体是新的正在执行(em.find(entity)==null)?em.persist(entity):em.merge(entity)

但弹簧数据仅供save()方法,有没有办法填补Persistable.isNew()方法与find()方法。

相关问题