2

リレーションの片側のみが永続コンテキストで管理されているのに、双方向リレーションの要素を削除できるのはなぜですか (例 I)? 機能しない一方向のリレーションシップがある場合 (例 II を参照)。なんで?

エンティティ:

@Entity
Class User {
    ...
    @OneToMany(mappedBy = "user")
    private List<Process> processes;

    @OneToOne // Unidirectional
    private C c;
    ...

    @PreRemove
    private void preRemove() {
        for (Process p : processes) {
            p.internalSetUser(null);
        }
    }
   ...
}

@Entity
Class Process {
    ...
    @ManyToOne
    private User user;
    ...

    @PreRemove
    protected void preRemove() {
        if (this.user != null) {
            user.internalRemoveProcess(this);
        }
    }
   ...
}

@Entity
Class C {

 }

例 I:

// Create User u1 with Processes p1, p2

tx.start();
// Only u1 is manged in persistence context and no process
userFacade.delete(u1); // There following is called: >> em.remove(em.merge(u1)); // Works
tx.commit();

例 II:

// Create User u and Object C c, establish their relation.

tx.start();
cFacade.remove(c); //>>MySQLIntegrityConstraintViolationException,foreign key constraint fails
ty.commit();

最初の例では、これらの内部メソッドを使用して、それぞれの場合に関係の反対側を設定しますが、この反対側は永続コンテキストで管理されていないと思いますか?! ユーザーのプロセスを変更してユーザーを保存すると、cascade.MERGEを使用しない限り、または両方がトランザクションに読み込まれてPCで管理されていない限り、プロセスは更新されません。では、なぜ除去が機能するのでしょうか。

4

2 に答える 2

1

これは予想される動作です。

関係は双方向であるため、アプリケーションが関係の一方を更新すると、もう一方も更新され、同期する必要があります。JPA では、一般に Java と同様に、関係を維持するのはアプリケーションまたはオブジェクト モデルの責任です。アプリケーションが関係の一方に追加する場合は、もう一方に追加する必要があります。

これは、関係の両側を処理するオブジェクト モデルのメソッドを追加または設定することで解決できるため、アプリケーション コードでこれを気にする必要はありません。これには 2 つの方法があります。リレーションシップ メンテナンス コードをリレーションシップの一方の側にのみ追加し、一方の側からのみセッターを使用するか (反対側を保護するなど)、または両方に追加して、無限ループを避けるようにしてください。

例えば:

public class Employee {
    private List phones;
    ...
    public void addPhone(Phone phone) {
        this.phones.add(phone);
        if (phone.getOwner() != this) {
            phone.setOwner(this);
        }
    }
    ...
}

ソース: OneToMany#Getters_and_Setters

于 2012-08-24T23:37:58.010 に答える
1

user.setC(null)例 II では、 を削除する前に呼び出す必要があると思いますc

例Iでは、これが私の理解です。最初にマージしてu1いるので、 au1'が PC にロードされ、 の状態u1がコピーされu1'ます (カスケードしていないためMERGE、これですべてです)。その後、返されます。次に、 remove (on u1') を呼び出すと、preRemoveが呼び出されて と が変更p1'されp2'ます。したがって、それらはダーティであり、フラッシュ時に適切に更新され (FK を に設定NULL)、u1'削除されます。そして、すべてが機能します。

念のため、JPA 2.0 仕様のマージ操作のセマンティクスを次に示します。

3.2.7.1 切り離されたエンティティ状態のマージ

マージ操作により、切り離されたエンティティから、エンティティ マネージャによって管理される永続エンティティへの状態の伝播が可能になります。

エンティティ X に適用されるマージ操作のセマンティクスは次のとおりです。

  • Xが切り離されたエンティティである場合、 の状態が同じ ID のX既存のマネージド エンティティ インスタンスにコピーされる か、または の新しいマネージド コピーが作成されます。X'X'X
  • が新しいエンティティ インスタンスの場合X、新しい管理エンティティ インスタンスX'が作成され、 の状態がX新しい管理エンティティ インスタンスにコピーされます X'
  • Xが削除されたエンティティ インスタンスである場合、マージIllegalArgumentException操作によって がスローされます (または、トランザクションのコミットが失敗します)。
  • が管理対象エンティティである場合、マージ操作によって無視されますが、これらの関係がカスケード要素値 または注釈で注釈付けされている場合X、マージ操作は関係によって参照されるエンティティにカスケードされ ます。Xcascade=MERGEcascade=ALL
  • カスケード要素値 orを持つY関係によって参照されるすべてのエンティティは、として再帰的にマージされます。 によって参照されるそのようなすべてについて、は reference に設定されます。(が管理されている場合、は と同じオブジェクトである ことに注意してください。)Xcascade=MERGEcascade=ALLYY'YXX'Y'XXX'
  • Xにマージされたエンティティであり、X'別のエンティティへの参照が Yあり、cascade=MERGEまたは cascade=ALLが指定されていない場合、 からの同じ関連付けのナビゲーションは、 と同じ永続 ID を持つX'管理対象オブジェクトへの参照を生成します。Y'Y

持続性プロバイダーは、フェッチされていない LAZY とマークされたフィールドをマージしてはなりません。マージ時にそのようなフィールドを無視する必要があります。

エンティティによって使用されるすべてVersionの列は、マージ操作中および/またはフラッシュ時またはコミット時に永続化ランタイム実装によってチェックされる必要があります。列がない場合Version、マージ操作中に永続化プロバイダ ランタイムによって実行される追加のバージョン チェックはありません。

参照

  • JPA 2.0 仕様
    • 3.2.7.1 切り離されたエンティティ状態のマージ
于 2010-08-12T23:06:57.953 に答える