问题与下面的问题基本相同:
JPA cascade persist and references to detached entities throws PersistentObjectException. Why?
我正在创建一个引用现有分离的实体的新实体。现在,当我在Spring数据存储库中保存此实体时,会抛出异常:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist
如果我们看一下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);
}
}
如果我们在AbstractEntityInformation
public boolean isNew(T entity) {
return getId(entity) == null;
}
所以基本上如果我保存()一个新实体(id == null)
,spring数据将始终调用persist,因此这种情况总是会失败。
在向集合添加新项目时,这似乎是一个非常典型的用例。
我该如何解决这个问题?
编辑1:
注:
此问题与How to save a new entity that refers existing entity in Spring JPA?没有直接关系。详细说明假设您获得了通过http创建新实体的请求。然后,您从请求中提取信息,并创建您的实体和现有的实体。因此,他们将永远脱离。
答案 0 :(得分:12)
我有一个类似的问题,我试图用一个已保存的实体对象保存一个新的实体对象。
我所做的是Persistable&lt; T>并相应地实现了isNew()。
public class MyEntity implements Persistable<Long> {
public boolean isNew() {
return null == getId() &&
subEntity.getId() == null;
}
或者您可以使用AbstractPersistable并覆盖那里的isNew。
我不知道这是否会被认为是处理这个问题的一种好方法,但它对我来说非常好,而且感觉非常自然。
答案 1 :(得分: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;
}
我们检查是否存在问题,如果是,只需通过其id从数据库重新加载现有实体,并将新实体的字段设置为此新加载的实例。然后它将被附加。
这要求新实体的服务保存对引用实体的服务的引用。这应该不是问题,因为您仍然使用spring,以便可以将服务添加为新的@Autowired
字段。
然而,另一个问题(在我的情况下,这种行为实际上是需要的),您在保存新实体的同时无法同时更改引用的现有实体。所有这些变化都将被忽略。
重要提示:
在许多情况下,可能是你的情况,这可能会简单得多。您可以向服务添加实体管理器的引用:
@PersistenceContext
private EntityManager entityManager;
并在上面if(){}
阻止使用
containable = entityManager.merge(containable);
而不是我的代码(如果有效则未经测试)。
在我的例子中,这些类是抽象的,而targetEntity
中的@ManyToOne
也是抽象的。直接调用entityManager.merge(可包含)然后会导致异常。但是,如果你的课程都是具体的,那么这应该有效。
答案 2 :(得分:2)
我对@EmbeddedId
和商家数据作为身份的一部分存在同样的问题。
了解实体是否为新的唯一方法是执行(em.find(entity)==null)?em.persist(entity):em.merge(entity)
但是spring-data仅提供save()
方法,并且无法使用Persistable.isNew()
方法填充find()
方法。