使用SpringData2JParepository双重保存新实体实例是否正确?

使用SpringData2JParepository双重保存新实体实例是否正确?,spring,spring-data-jpa,spring-data,Spring,Spring Data Jpa,Spring Data,我有两个实体处于双向多对多关系中 A多对多B 我有一个端点,客户机可以在这里创建a的实例,同时通过传入一个B实体id键数组,向a添加一些B实体。请记住这些B实体已经存在于数据库中。没有任何业务或软件设计案例可以将它们的创建与应用程序的创建紧密耦合 类A看起来像这样,而B是一样的,但是引用了A @Entity class A { @Id @GeneratedValue int id; @ManyToMany List<B> bs; S

我有两个实体处于双向多对多关系中

A多对多B

我有一个端点,客户机可以在这里创建a的实例,同时通过传入一个B实体id键数组,向a添加一些B实体。请记住这些B实体已经存在于数据库中。没有任何业务或软件设计案例可以将它们的创建与应用程序的创建紧密耦合

类A看起来像这样,而B是一样的,但是引用了A

@Entity
class A {
    @Id
    @GeneratedValue
    int id;

    @ManyToMany
    List<B> bs;

    String someValue;

    int someValue2;

    // With some getters and setters omitted for brevity
}
客户端将提交一个类似这样的JSON请求来创建一个新的a,其中包含指向id为3的B和id为4的B的链接

 {
     "bs": [{id:3}, {id:10}],
     "someValue": "not important",
     "someValue2": 1
 }
好了,一切正常,我看到所有字段都反序列化了,好了,然后我用

aRepository.save(aToCreate);
这很有效。。。除了我需要与b实体实例关联的所有数据这一事实之外,由
aRepository.save()
返回的A对象只填充了A上的自动填充字段,对b实体没有做任何操作。它们仍然只是只设置了ID的空心实体

所以我四处看看,显然SimpleParepository就是这么做的

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

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}
这很好用。第二次通过存储库服务时,它合并而不是持久化,因此B关系得到了补充

我的问题是:这是正确的,还是我能做些别的事情,让自己看起来不那么不善言辞和糟糕


需要明确的是,这只在创建全新的实例时才重要,而且一旦a在数据库中,这就不再是问题了,因为SimpleParepository将流入
em.merge()
代码行。此外,我还尝试了不同的CascadingType注释,但它们都不是我想要的。级联是关于将父实体的子实体视图的状态持久化到其子实体,但我想做的是在创建新实例时将子实体水合,而不必两次访问数据库。

对于新实例,
aToCreate
savedA
是同一个实例,因为这是JPA规范的内容:

使实例得到管理和持久化

Spring数据只返回相同的实例,因此persist/merge可以抽象为一个方法

如果您希望与A关联的B实例是现有实体,那么您需要获取对这些现有实例的引用并将其设置在A上。您可以使用Spring Data的JpaRepository的
T getOne(ID ID)
方法在不命中数据库的情况下执行此操作:


您可以在您的控制器中或通过自定义反序列化程序执行此操作。

这就是我最终使用的。这使调用方能够在一次调用中保存和添加实例,并解释了到底发生了什么。我的所有存储库实例现在都扩展了这个基本实例

public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {

    /**
     * Saves an instance twice so that it's forced to persist AND then merge. This should only be used for new detached entities that need to be saved, and who also have related entities they want data about hydrated into their object.
     */
    @Transactional
    default T saveAndHydrate(T save) {
        return this.save(this.save(save));
    }
}
public interface BaseRepository扩展了JpaRepository{
/**
*将一个实例保存两次,以便强制将其持久化,然后合并。这仅适用于需要保存的新分离实体,以及需要将相关实体的数据添加到其对象中的实体。
*/
@交易的
默认T保存和水合物(T保存){
返回this.save(this.save(save));
}
}
public A createA(@RequestBody A aToCreate) {
    A savedA = aRepository.save(aRepository.save(aToCreate));
    savedA.getbs().forEach(b -> Service.callWithBValue(b.getImportantValue());
}
public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {

    /**
     * Saves an instance twice so that it's forced to persist AND then merge. This should only be used for new detached entities that need to be saved, and who also have related entities they want data about hydrated into their object.
     */
    @Transactional
    default T saveAndHydrate(T save) {
        return this.save(this.save(save));
    }
}