web-dev-qa-db-ja.com

Spring Data JPAリポジトリでsave()の後に返されたインスタンスを使用するのはなぜですか?

コードは次のとおりです。

@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}

JpaRepository Spring Data JPAプロジェクトから。

テストコードは次のとおりです。

public class JpaAccountRepositoryTest extends JpaRepositoryTest {
    @Inject
    private AccountRepository accountRepository;

    @Inject
    private Account account;

    @Test
    @Transactional
    public void createAccount() {
        Account returnedAccount = accountRepository.save(account);

        System.out.printf("account ID is %d and for returned account ID is %d\n", account.getId(), returnedAccount.getId());
    }
}

結果は次のとおりです。

account ID is 0 and for returned account ID is 1

これは、CrudReporsitory.save()javadocからのものです。

指定されたエンティティを保存します。保存操作によってエンティティインスタンスが完全に変更された可能性があるため、返されたインスタンスを以降の操作に使用します。

Spring Data JPAのSimpleJpaRepositoryの実際のコードは次のとおりです。

 @Transactional
    public T save(T entity) { 
            if (entityInformation.isNew(entity)) {
                    em.persist(entity);
                    return entity;
            } else {
                    return em.merge(entity);
            }
    }

だから、問題は元のインスタンスの代わりに返されたインスタンスを使用する必要があるのですか?はい

元のEntityManager.persist()メソッドはvoidを返すため、インスタンスは永続コンテキストにアタッチされます。リポジトリに保存するアカウントを渡すときに、プロキシマジックが発生しますか? Spring Data JPAプロジェクトのアーキテクチャの制限ですか?

55
akazlou

CrudRepositoryインターフェイスのsave(…)メソッドは、エンティティの状態に関係なく、単純にエンティティを格納することを抽象化することになっています。したがって、( JPA)ストアは、保存される新しいエンティティと更新される既存のエンティティを区別します。そのため、メソッドは実際にsave(…)create(…)ではなくupdate(…)と呼ばれます。 merge(…)が呼び出されたときにJPAが潜在的に行うように、ストア実装が完全に異なるインスタンスを返すことを実際に許可するために、そのメソッドから結果を返します。

そのため、一般的に、実際の実装に関して寛容(許容、許容)であるため、JPAのメソッドを実装するのはAPIの決定です。渡されたエンティティに対して行われる追加のプロキシメッセージングは​​ありません。

60
Oliver Drotbohm

2番目の部分を見逃しました:エンティティが新しいものではない場合、mergeが呼び出されます。 mergeは、引数の状態を同じIDの添付エンティティにコピーし、添付エンティティを返します。エンティティが新しくなく、返されたエンティティを使用しない場合、切り離されたエンティティに変更を加えます。

15
JB Nizet