Java Spring-Data JPA:保存引用现有实体的新实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16559407/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring-Data JPA: save new entity referencing existing one
提问by beginner_
The question is basically the same as below one:
问题与以下问题基本相同:
JPA cascade persist and references to detached entities throws PersistentObjectException. Why?
JPA 级联持久化和对分离实体的引用抛出 PersistentObjectException。为什么?
I'm creating a new entity that references an existing, detached one. Now when I save this entity in my spring data repository an exception is thrown:
我正在创建一个新实体,它引用一个现有的、分离的实体。现在,当我将此实体保存在 Spring 数据存储库中时,会引发异常:
org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist
if we look at the save() method in source code of spring data JPA we see:
如果我们查看 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);
}
}
and if we look at isNew() in AbstractEntityInformation
如果我们看一下 isNew() AbstractEntityInformation
public boolean isNew(T entity) {
return getId(entity) == null;
}
So basically if i save() a new entity (id == null)
, spring data will always call persist and hence this scenario will always fail.
所以基本上如果我 save() 一个新实体(id == null)
,spring data 将始终调用persist,因此这种情况将始终失败。
This seems to be a very typical use case when adding new items to collections.
在向集合中添加新项目时,这似乎是一个非常典型的用例。
How can I resolve this?
我该如何解决这个问题?
EDIT 1:
编辑 1:
NOTE:
笔记:
This issue is NOT directly related to How to save a new entity that refers existing entity in Spring JPA?. To elaborate assume you get the request to create the new entity over http. You then extract the information from the request and create your entity and the existing referenced one. Hence they will always be detached.
此问题与如何保存引用 Spring JPA 中现有实体的新实体没有直接关系?. 为了详细说明,假设您收到了通过 http 创建新实体的请求。然后您从请求中提取信息并创建您的实体和现有的引用实体。因此,它们将永远是分离的。
采纳答案by beginner_
The best I came up with is
我想出的最好的是
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;
}
We check if we are in the problematic situation and if yes just reload the existing entity from the database by its id and set the field of the new entity to this freshly loaded instance. It then will be attached.
我们检查我们是否处于有问题的情况,如果是,只需通过其 id 从数据库重新加载现有实体,并将新实体的字段设置为这个新加载的实例。然后它会被附加。
This requires that the service for new entity holds a reference to the service of the referenced entity. This should not be an issue since you are using spring anyway so that service can just be added as a new @Autowired
field.
这要求新实体的服务持有对被引用实体的服务的引用。这应该不是问题,因为无论如何您都在使用 spring,因此可以将服务添加为新@Autowired
字段。
Another issue however (in my case this behavior is actually desired) that you can't change the referenced existing entity at the same time while saving the new one. All those changes will be ignored.
然而,另一个问题(在我的情况下实际上需要这种行为),您无法在保存新实体的同时更改引用的现有实体。所有这些更改都将被忽略。
IMPORTANT NOTE:
重要的提示:
In many and probably your cases this can be much simpler. You can add a reference of entity manager to your service:
在许多情况下,可能是您的情况,这可以简单得多。您可以将实体管理器的引用添加到您的服务中:
@PersistenceContext
private EntityManager entityManager;
and in above if(){}
block use
并在上面的if(){}
块中使用
containable = entityManager.merge(containable);
instead of my code (untested if it works).
而不是我的代码(如果它有效,则未经测试)。
In my case the classes are abstract and targetEntity
in @ManyToOne
is hence abstract too. Calling entityManager.merge(containable) directly then leads to an exception. However if your classes are all concrete this should work.
在我的情况下,类是抽象的,因此targetEntity
in@ManyToOne
也是抽象的。直接调用 entityManager.merge(containable) 会导致异常。但是,如果您的课程都是具体的,这应该可行。
回答by Jonas Geiregat
I had a similar issue where I was trying to save an new entity object with an already saved entity object inside.
我有一个类似的问题,我试图用一个已经保存的实体对象来保存一个新的实体对象。
What I did was implemented Persistable< T > and implemented isNew() accordingly.
我所做的是实现Persistable< T > 并相应地实现了 isNew() 。
public class MyEntity implements Persistable<Long> {
public boolean isNew() {
return null == getId() &&
subEntity.getId() == null;
}
Or you could use AbstractPersistableand override the isNew there ofcourse.
或者你可以使用AbstractPersistable并覆盖 isNew 那里当然。
I don't know if this will be considered a good way of handling this issue but it worked out quite good for me and besides feels very natural to do.
我不知道这是否会被认为是处理这个问题的好方法,但它对我来说非常好,而且感觉很自然。
回答by nakkun
I have the same problem with a @EmbeddedId
and business data as part of the id.
The only way to know if the entity is new is performing a (em.find(entity)==null)?em.persist(entity):em.merge(entity)
@EmbeddedId
作为 id 的一部分,我对 a和业务数据有同样的问题。
了解实体是否为新实体的唯一方法是执行(em.find(entity)==null)?em.persist(entity):em.merge(entity)
but spring-data only provides save()
method and there is no way to fill the Persistable.isNew()
method with a find()
method.
但是 spring-data 只提供save()
方法,没有办法Persistable.isNew()
用find()
方法填充方法。