1 対多の関係 (国->
) の非常に単純な例を取り上げます。
国(裏面):
@OneToMany(mappedBy = "country", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<StateTable> stateTableList=new ArrayList<StateTable>(0);
StateTable (所有側) :
@JoinColumn(name = "country_id", referencedColumnName = "country_id")
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
private Country country;
StateTable
アクティブなデータベース トランザクション (JTA またはリソース ローカル) 内で提供された (デタッチされた) エンティティを更新しようとするメソッド:
public StateTable update(StateTable stateTable) {
// Getting the original state entity from the database.
StateTable oldState = entityManager.find(StateTable.class, stateTable.getStateId());
// Get hold of the original country (with countryId = 67, for example).
Country oldCountry = oldState.getCountry();
// Getting a new country entity (with countryId = 68) supplied by the client application which is responsible for modifying the StateTable entity.
// Country has been changed from 67 to 68 in the StateTable entity using for example, a drop-down list.
Country newCountry = entityManager.find(Country.class, stateTable.getCountry().getCountryId());
// Attaching a managed instance to StateTable.
stateTable.setCountry(newCountry);
// Check whether the supplied country and the original country entities are equal.
// (Both not null and not equal - http://stackoverflow.com/a/31761967/1391249)
if (ObjectUtils.notEquals(newCountry, oldCountry)) {
// Remove the state entity from the inverse collection held by the original country entity.
oldCountry.remove(oldState);
// Add the state entity to the inverse collection held by the newly supplied country entity
newCountry.add(stateTable);
}
return entityManager.merge(stateTable);
}
orphanRemoval
に設定されていることに注意してくださいtrue
。StateTable
エンティティは、エンティティの関連付け ( ) を別のもの ( ) に変更することに関心のあるクライアント アプリケーションによって提供されます(Country
したがってcountryId = 67
、StateTable
JPAcountryId = 68
の逆側で、子エンティティをその親 (コレクション) から別の親 (コレクション) に移行します)。orphanRemoval=true
逆に反対します)。
Hibernate プロバイダーはDELETE
DML ステートメントを発行し、StateTable
エンティティに対応する行を基になるデータベース テーブルから削除します。
orphanRemoval
が に設定されているにもかかわらず、true
Hibernate が通常のUPDATE
DML ステートメントを発行orphanRemoval
し、リレーションシップ リンクが移行される (単に削除されるのではなく) ため、 の効果が完全に中断されることを期待しています。
EclipseLink はまさにその仕事をします。指定されたシナリオでステートメントを発行します( set toUPDATE
と同じ関係を持ちます)。orphanRemoval
true
仕様に従って動作しているのはどれですか? この場合、逆側からUPDATE
削除する以外に、Hibernate にステートメントを発行させることは可能ですか?orphanRemoval
これは、双方向の関係を両側でより一貫性のあるものにするための試みにすぎません。
add()
上記のスニペットで使用されている防御リンク管理メソッドremove()
は、必要に応じて、Country
エンティティで次のように定義されます。
public void add(StateTable stateTable) {
List<StateTable> newStateTableList = getStateTableList();
if (!newStateTableList.contains(stateTable)) {
newStateTableList.add(stateTable);
}
if (stateTable.getCountry() != this) {
stateTable.setCountry(this);
}
}
public void remove(StateTable stateTable) {
List<StateTable> newStateTableList = getStateTableList();
if (newStateTableList.contains(stateTable)) {
newStateTableList.remove(stateTable);
}
}
アップデート :
UPDATE
指定されたコードが次のように変更されている場合、Hibernate は予期される DML ステートメントのみを発行できます。
public StateTable update(StateTable stateTable) {
StateTable oldState = entityManager.find(StateTable.class, stateTable.getStateId());
Country oldCountry = oldState.getCountry();
// DELETE is issued, if getReference() is replaced by find().
Country newCountry = entityManager.getReference(Country.class, stateTable.getCountry().getCountryId());
// The following line is never expected as Country is already retrieved
// and assigned to oldCountry above.
// Thus, oldState.getCountry() is no longer an uninitialized proxy.
oldState.getCountry().hashCode(); // DELETE is issued, if removed.
stateTable.setCountry(newCountry);
if (ObjectUtils.notEquals(newCountry, oldCountry)) {
oldCountry.remove(oldState);
newCountry.add(stateTable);
}
return entityManager.merge(stateTable);
}
新しいバージョンのコードで次の 2 行を確認します。
// Previously it was EntityManager#find()
Country newCountry = entityManager.getReference(Country.class, stateTable.getCountry().getCountryId());
// Previously it was absent.
oldState.getCountry().hashCode();
最後の行が存在しないか、またはEntityManager#getReference()
に置き換えられているEntityManager#find()
場合、DELETE
DML ステートメントが予期せず発行されます。
それで、ここで何が起こっているのですか?特に携帯性を重視しています。この種の基本機能をさまざまな JPA プロバイダーに移植しないと、ORM フレームワークの使用が著しく損なわれます。
と の基本的な違いを理解していEntityManager#getReference()
ますEntityManager#find()
。