21

次のエンティティ クラスと双方向の 1 対多の関係があります。

0 または 1 クライアント <-> 0 以上の製品注文

クライアントエンティティを永続化する場合、関連する製品注文エンティティも永続化する必要があります (「親」クライアントへの外部キーが更新されている可能性があるため)。

もちろん、必要な CASCADE オプションはすべてクライアント側で設定されます。ただし、次のシナリオのように、既存の製品注文を参照しながら、新しく作成されたクライアントが初めて保持される場合は機能しません。

  1. 製品注文 '1' が作成され、保持されます。正常に動作します。
  2. クライアント '2' が作成され、製品注文 '1' がその製品注文リストに追加されます。その後、永続化されます。動作しません。

いくつかのアプローチを試しましたが、どれも期待した結果を示しませんでした。以下の結果を参照してください。ここで関連するすべての質問を読みましたが、役に立ちませんでした。私は、EclipseLink 2.3.0、純粋な JPA 2.0 アノテーション、および GlassFish 3.1.2 上の Apache Derby (JavaDB) インメモリ DB のトランザクション タイプとして JTA を使用しています。エンティティ関係は、JSF GUI によって管理されます。オブジェクト レベルの関係管理は機能します (永続化は別として)。JUnit テストでテストしました。

アプローチ 1) 「デフォルト」 (NetBeans クラス テンプレートに基づく)

クライアント:

@Entity
public class Client implements Serializable, ParentEntity {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy = "client", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},
            fetch= FetchType.LAZY)
    private List<ProductOrder> orders = new ArrayList<>();

    // other fields, getters and setters
}

製品注文:

@Entity
public class ProductOrder implements Serializable, ChildEntity {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne // owning side
    private Client client;

    // other fields, getters and setters
}

汎用持続性ファサード:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().persist(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

結果:

create() は、この例外をすぐにスローします。

警告: EJB ClientFacade メソッドの呼び出し中にシステム例外が発生しました public void javaee6test.beans.AbstractFacade.create(java.lang.Object) javax.ejb.EJBException: トランザクションが中止されました ...

原因: javax.transaction.RollbackException: ロールバックのマークが付けられたトランザクション。...

原因: 例外 [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would 'PRODUCTORDER' で定義された 'SQL120513133540930' で識別される一意または主キー制約または一意のインデックスで重複キー値が発生しました。エラー コード: -1 呼び出し: INSERT INTO PRODUCTORDER (ID, CLIENT_ID) VALUES (?, ?) bind => [2 つのパラメーター バインド] クエリ: InsertObjectQuery(javaee6test.model.ProductOrder[ id=1 ]) ...

原因: java.sql.SQLIntegrityConstraintViolationException: 'PRO-DUCTORDER' で定義された 'SQL120513133540930' で識別される一意または主キーの制約または一意のインデックスでキー値が重複する可能性があるため、ステートメントは中止されました。...

原因: org.apache.derby.client.am.SqlException: 'SQL120513133540930' で定義された'SQL120513133540930' で識別される一意または主キーの制約または一意のインデックスで重複キー値が発生する可能性があるため、ステートメントは中止されましたPRODUCTOR-DER'.

この例外がわかりません。edit() は正常に動作します。しかし、作成時にクライアントに製品注文を追加したいので、これでは不十分です。

アプローチ 2) merge() のみ

Generic Persistence Facade への変更:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().merge(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

結果:

create() で、EclipseLink Logging の出力には次のように表示されます。

罰金: INSERT INTO CLIENT (ID, NAME, ADDRESS_ID) VALUES (?, ?, ?) bind => [3 つのパラメーターがバインドされている]

ただし、製品注文表には「更新」はありません。したがって、関係は確立されません。一方、edit() は問題なく動作します。

アプローチ 3) 両方のエンティティ タイプの ID GenerationType.IDENTITY

クライアントと製品注文クラスの両方への変更:

...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...

結果:

create() で、EclipseLink Logging の出力には次のように表示されます。

罰金: INSERT INTO CLIENT (NAME, ADDRESS_ID) VALUES (?, ?) bind => [2 つのパラメーターがバインドされている]

ファイン: 値 IDENTITY_VAL_LOCAL()

罰金: INSERT INTO PRODUCTORDER (ORDERDATE, CLIENT_ID) VALUES (?, ?) bind => [2 つのパラメーターがバインドされている]

ファイン: 値 IDENTITY_VAL_LOCAL()

したがって、クライアントのリストに追加された製品注文との関係を確立する代わりに、新しい製品注文エンティティが作成されて永続化され (!)、そのエンティティとの関係が確立されます。ここでも同じ、 edit() は正常に動作します。

アプローチ 4) アプローチ (2) と (3) を組み合わせる

結果: アプローチ (2) と同じ。

私の質問は、上記のシナリオを実現する方法はありますか? どのようにアーカイブできますか?JPA を使い続けたいです (ベンダー固有のソリューションはありません)。

4

4 に答える 4

8

こんにちは、今日も同じ問題がありました。openJPA メーリング リストに次のメールを送信してください。

やあ。同じエンティティでの参照の挿入と更新に問題があります。

別のオブジェクト (Person) への参照を持つ新しいオブジェクト (Exam) を挿入しようとしていますが、同時に Person オブジェクトの属性 (birthDate) を更新したいと考えています。CascadeType を ALL に設定しても、更新は行われません。これが機能する唯一の方法は、永続化を行い、その後マージ操作を行うことです。これは正常ですか?私は何かを変更する必要がありますか??

ユーザーが更新したいオブジェクト (Exam の子オブジェクト) の数がわからないため、Person オブジェクトでマージを使用する「手動更新」のアイデアは好きではありません。

エンティティ:

public class Exam{  
   @ManyToOne(cascade= CascadeType.ALL)
   @JoinColumn(name = "person_id")
   public Person person;
......
}

public class Person{
    private Date birthDate;
   @OneToMany(mappedBy = "person")
    private List<Exam> exams
.......
}

public class SomeClass{
   public void someMethod(){
      exam = new Exam()
      person.setBirthDate(new Date());
      exam.setPerson(person); 
      someEJB.saveExam(exam);
   }
}

public class someEJB(){

   public void saveExam(Exam exam){
        ejbContext.getUserTransaction().begin();
        em.persist(exam);
        //THIS WORKS
        em.merge(exam.getPerson());
        ejbContext.getUserTransaction().commit();       
   }

}

すべての子オブジェクトに MERGE メソッドを使用する必要がありますか?

そして答えはこうでした。

問題は、Exam が新しく、Person が存在し、永続化操作をカスケードするときに既存のエンティティが無視されることです。これは期待どおりに機能していると思います。

リレーションシップが CascadeType.ALL に設定されている限り、いつでも em.persist(exam); を変更できます。em.merge(exam); に。これにより、新しい試験が永続化され、マージ呼び出しがその人にカスケードされます。

ありがとう、リック


これがお役に立てば幸いです。

于 2013-04-08T17:44:24.487 に答える
1

関係の両側を設定していることを確認してください。注文をクライアントに追加するだけではなく、注文のクライアントも設定する必要があります。また、変更した両方のオブジェクトをマージする必要があります。クライアントをマージするだけでは、注文のクライアントはマージされません (ただし、カスケードによってマージされます)。

persist は機能しません。persist は、永続化されるオブジェクトが永続化コンテキストに対して正しい必要があるためです。つまり、切り離されたオブジェクトを参照せず、管理対象オブジェクトを参照する必要があります。

あなたの問題は、オブジェクトを切り離すことに起因します。オブジェクトを切り離さなければ、同じ問題は発生しません。通常、JPA では、EntityManager を作成し、オブジェクトを検索/クエリし、それらを編集/永続化し、コミットを呼び出します。マージは必要ありません。

于 2012-06-14T14:37:08.103 に答える
0

ProductOrder エンティティで @Joincolumn アノテーションを使用します。以下を参照してください。

   @Entity
   public class ProductOrder implements Serializable, ChildEntity {
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @ManyToOne // owning side
   @JoinColumn(name = "PRODUCTORDER_ID", referencedColumnName = "ID")
   //write your database column names into name and referencedColumnName attributes.
   private Client client;

   // other fields, getters and setters
   }
于 2012-06-14T10:34:03.767 に答える