web-dev-qa-db-ja.com

Spring Data JPA-返されたエンティティへの変更が自動的に永続化されるのはなぜですか?

例を挙げて質問を提示します。

以下のようなリポジトリがあることを表明します。

_public interface ExampleObjectRepository extends CrudRepository<ExampleObject, Long> {

}
_

JpaRepositoryインターフェースを拡張することにより、ExampleObjectリポジトリーは以下のメソッドを継承します。

_T findOne(ID id);
_

ここで、このメソッドの呼び出し後にExampleObjectへの参照を受け取った場合、このメソッドに対して行った操作はすべて自動的にデータベースに保存されることを確認しました。次に例を示します。

_ExampleObject pointInCase = exampleObjectRepository.findOne(1L);
pointInCase.setName("Something else");
_

主題について読んで、私はこれがExampleObjectインスタンスが_not detached_であることを意味することを理解しています。

これは私の期待に反します。変更を保存するには、CrudRepositoryから継承されたsaveメソッドを使用する必要があると予想していました。

_T save(T entity);
_

Spring Data JPAリポジトリから返されたオブジェクトが標準でアタッチされたままであることを確認し、APIを使用して、デタッチされた参照のみを返すようにリポジトリ内のメソッドをマークする方法を説明してくれる人はいますか?

エンティティの状態を変更すると、前述のsave(T entity)メソッドで使用したときにその定義も変更される可能性があると思います。したがって、更新のIDがどのように処理されるかについても理解していただければ幸いです。

31
8bitjunkie

これがJPAの基本原則です。アタッチされた(管理された)エンティティを操作すると、これらの管理されたエンティティに対して行われたすべての変更は自動的に永続化されます。

変更を永続的にしたくない場合は、変更を加えたり、トランザクションをロールバックしたりしないでください。

切り離されたエンティティでの作業は、すべての関連付けの遅延読み込みを妨げるため、悪夢になります。エンティティではいつでもEntityManager.detach()を呼び出すことができますが、実際にはそうしません。それがどのように機能するかを理解し、それに対処するようにしてください。不利な点よりもはるかに多くの利点があります。そのうちの1つは、複雑なビジネスロジックが行う可能性のあるすべての変更を保存することを考える必要がないことです。これは、すべてJPAによって透過的に行われるためです。

21
JB Nizet

トランザクションがリポジトリにスコープされている場合は、リポジトリがデタッチされたエンティティを返すようにすることができます。つまりトランザクションはリポジトリへの入り口から始まり、コードがリポジトリを出ると閉じられます。

このように作業する場合は、必要なすべてのデータが一度にロードされるように注意する必要があります。そうしないと、遅延初期化例外が発生するためです。しかし、多くの場合、これは、いつどのデータをロードするかをある程度制御できるため、望ましいと思います。そうしないと、JPAを使用するときに指をすり抜けてしまいます。

変更には、ロード、変更(および暗黙的に永続化)の完全なプロセスをラップする別のトランザクションスコープを使用します。

5
Jens Schauder

私も同じ問題に直面しました。
しかし、私にとっては、問題はカスタムリポジトリの実装にあり、@ Transactional(readOnly = true)を追加しました。

1
Sandy