1対多の関係(国->
状態)の非常に簡単な例をとります。orphanRemovalがtrueに設定されたエンティティアソシエーションを持つエンティティをマージしている間、Hibernateが孤立したエンティティを削除しないようにする
国(逆側):
@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
に変更することに関心のあるクライアントアプリケーションから提供されます(したがって、JPAの逆の側で、子エンティティを親(コレクション)から別のもの親(コレクション)orphanRemoval=true
が順番に反対します)。
Hibernateプロバイダは、DML文を発行して、StateTable
エンティティに対応する行を基になるデータベーステーブルから削除します。
orphanRemoval
がtrue
に設定されているという事実にもかかわらず、私は(単に削除されていない)関係のリンクが移行されるため、orphanRemoval
の効果が完全に懸濁させる通常のUPDATE
DML文を発行するために休止状態を期待しています。
EclipseLinkはまさにその仕事をします。指定されたシナリオ(と同じ関係を持つ、true
に設定)でUPDATE
ステートメントを発行します。
仕様によってどちらが動作していますか?この場合、Hibernateが逆の側からorphanRemoval
を取り除く以外にUPDATE
文を発行することは可能ですか?
これは、両側の双方向の関係がより一貫性にする唯一の試みです。
防御リンク管理方法、すなわち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);
}
}
更新:与えられたコードは次のように変更された場合
Hibernateは唯一、予想UPDATE
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()
がDELETE
DML文が予期せずに発行され、その後、EntityManager#find()
に置き換えられます。いずれかの場合
ここでは何が起こっていますか?特に、私は移植性を重視しています。この種類の基本を異なるJPAプロバイダに移植しないと、ORMフレームワークの使用が厳しく敗北します。
私はEntityManager#getReference()
とEntityManager#find()
の基本的な違いを理解しています。
削除する前に追加すると、ちょっと考えても違いはありますか? –
これらの2行の順序を変更しても違いはありません。私はあらかじめそのような可能性をチェックしています:) – Tiny
Hibernateのバグのようです。ちょうど推測では、新しい国を明示的にマージし、その後、国家の前の古い国をマージします。必要に応じて、間にflushを呼び出します。 – BalusC