Spring-Data JPA:保存引用现有实体的新实体 [英] Spring-Data JPA: save new entity referencing existing one

查看:193
本文介绍了Spring-Data JPA:保存引用现有实体的新实体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The question is basically the same as below one:

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

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

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:

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

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

if we look at the save() method in source code of spring data JPA we see:

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

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

如果我们在<$中查看isNew() c $ c> AbstractEntityInformation

public boolean isNew(T entity) {

    return getId(entity) == null;
}

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

So basically if i save() a new entity (id == null), spring data will always call persist and hence this scenario will always fail.

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

This seems to be a very typical use case when adding new items to collections.

如何解决此问题?

编辑1:

注意:

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

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.

推荐答案

我想出的最好的是

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设置,并将新实体的字段设置为此新加载的实例。然后它将被附加。

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.

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

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.

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

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.

重要提示:

在很多情况下,可能你的情况可能会很多简单。您可以向服务添加实体管理器的引用:

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;

以上 if(){} 阻止使用

containable = entityManager.merge(containable);

而不是我的代码(如果有效则未经测试)。

instead of my code (untested if it works).

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

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.

这篇关于Spring-Data JPA:保存引用现有实体的新实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆