2012-06-14 15 views
17

エンティティを永続化し、マージカスケード接続子実体:JPA:私は、次のエンティティクラスとの双方向の1対多の関係持っ

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

クライアントエンティティを永続化する場合、(親クライアントへの外部キーが更新されている可能性があるため)関連付けられたプロダクトオーダーエンティティを永続化します。

もちろん、すべての必要なCASCADEオプションはクライアント側で設定されています。

  1. 製品注文を「1」に作成され、永続化されている:このシナリオでは、既存の製品注文を参照しながら、新たに作成されたクライアントが初めて永続化されている場合でも、それは動作しません。正常に動作します。
  2. クライアント '2'が作成され、商品オーダーリストに商品オーダー '1'が追加されます。その後、それは永続化されます。動作しません。

私はいくつかのアプリを試しましたが、どれも期待された結果を示していませんでした。以下の結果を参照してください。私はここで関連するすべての質問を読んだが、彼らは私を助けなかった。 GlassFish 3.1.2のApache Derby(JavaDB)インメモリDBに、トランザクションの種類としてEclipseLink 2.3.0、JPA 2.0の純粋なアノテーション、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 
} 

productOrderは:

@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); 
} 

結果:

警告:システム例外は、EJB ClientFacade法に呼び出し中に発生します。public void javaee6test.beans.AbstractFacade.create(java.langで

(作成)はすぐに、この例外がスローされます.Object) javax.ejb.EJBException:トランザクションが中止されました...

原因: javax.transaction.RollbackException:トランザクションがロールバック用にマークされています。 ...

によって引き起こさ:例外[EclipseLinkの-4002](Eclipseの永続 サービス - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException内部 例外:java.sqlの。 SQLIntegrityConstraintViolationException: ステートメントが、重複したキー の値をユニークまたはプライマリキー制約またはユニークなインデックス で識別されたため、「PRODUCTORDER」に「SQL120513133540930」が定義されているため、中止されました。エラーコード:-1 呼び出し:INSERT INTO PRODUCTORDER(ID、CLIENT_ID)VALUES(?、?)bind => [2パラメータがバインドされました]クエリ: InsertObjectQuery(javaee6test.model.ProductOrder [id = 1])... java.sql.SQLIntegrityConstraintViolationException:それはPRO-に定義された 「SQL120513133540930」によって識別されるユニークな または主キー制約または一意のインデックスに重複キー値を引き起こしてしまうため、ステートメントが 中止されたことで引き起こさ

DUCTORDER '。 org.apache.derby.client.am.SqlException:によって引き起こさ...

文が こと-原因を中止されたことがユニークか 主キーCON-straintに重複キー値を引き起こしているだろうか'PRODUCTOR-DER'に定義された 'SQL120513133540930'によって識別される一意のインデックス。

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

アプローチ2))(一般的な持続性ファサードにのみ

変更マージ:

// 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); 
} 

結果:

オン()を作成し、EclipseLinkのログ出力は言う:

ファイン:INSERT INTO CLIENT(ID、NAME、ADDRESS_ID)VALUES(?、 ?、?) バインド=> [3パラメータバインド]

ただし、製品注文表では「更新」はありません。したがって、関係は確立されていません。一方、edit()はもう一方でうまくいきます。

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

結果:

には(作成)、EclipseLinkの両方のエンティティタイプの両方のクライアントと製品注文クラスに

変化に関する

Apporach 3)同上GenerationType.IDENTITYをログ出力は以下の通りです:

Fine:INSERT I NTOクライアント(NAME、ADDRESS_ID)VALUESバインド=> [2つの パラメータバインド]

ファイン(?):値IDENTITY_VAL_LOCAL()

ファイン:( productOrderは(ORDERDATE、CLIENT_ID)値にINSERT? 、?)バインド=> [バインド2つの パラメータ]

ファイン:代わりに、クライアントのリストに追加され、製品の順序に関係をestabilshingの値IDENTITY_VAL_LOCAL()

は、このように、新しいprodcut注文エンティティが作成され、永続化され(!)、そのエンティティとの関係が確立されます。ここでもedit()はうまく動作します。

Apporach 4)アプローチ(2)及び(3)

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

私の質問は上記のシナリオを実現する方法はありますか?それはどのように達成できますか?私はJPA(ベンダー固有の解決策なし)にとどまりたいです。

答えて

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 
    } 
+1

いいえ、これは動作しません。実際には何も変わらない。私は '@JoinColumn(name =" CLIENT_ID "、referencedColumnName =" ID ") 'で試しましたが、動作しませんでした。 – SputNick

0

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

persistは機能しません。永続化されるオブジェクトは永続コンテキストに対して正しいものである必要があります。つまり、デタッチされたオブジェクトを参照する必要はなく、管理対象オブジェクトを参照する必要があります。

あなたの問題は、あなたがオブジェクトを切り離すことから発生します。オブジェクトをデタッチしなかった場合、同じ問題は発生しません。通常JPAでは、EntityManagerを作成し、オブジェクトを検索/照会し、それらを編集/永続化し、コミットを呼び出します。マージは必要ありません。今日は同じ問題を抱えていた

+6

私はあなたのアドバイスに似た回避策を見つけました: 'カスケード'オプションを削除しました。そして、 "依存する"エンティティのリストを維持し、 "親"が永続化/合併した しかし、これは決して「公式JPA」apporachではないかもしれないので、私はこの答えを受け入れません。また、私はエンティティを明示的にデタッチしません。また、EclipseLinkのログには切り離し操作は含まれていません。 これらの 'カスケード'オプションは、明らかに機能しないので、私は理解していません。 – SputNick

+0

4年後、私はまったく同じことをしなければなりませんでした。最初は、JPAのみのカスケードアノテーションを使用するというバイラルのアプローチに従っていましたが、アノテーションを削除して親エンティティを永続化し、各子エンティティに追加して、マージを使用して永続化する必要がありました。 –

7

こんにちは、私はこのメールでOpenJPAのメーリングリストに尋ねる:

こんにちは。私は同じエンティティの参照の挿入と更新に問題があります。

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

ユーザーが更新したいオブジェクト(Examの子オブジェクト)の数がわからないため、私はPersonオブジェクトのmergeを使って "手動で更新する"というアイデアは気に入らない。

エンティティ:

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();  
    } 

} 

は、私はすべての子オブジェクトのマージ方法を使用する必要がありますか?

と答えこれだった:あなたの問題は、試験が新しいことで、まだ存続 操作をカスケード接続したときに人が 既存され、既存のエンティティは無視されますように見えます

。私はこれが期待通りに機能していると信じています。

あなたの関係がCascadeType.ALLに設定されている限り、いつでも あなたのem.persist(試験)を変更できます。 em.merge(試験)に。それで が新しい試験を継続し、 人にマージコールをカスケードします。

おかげで、 リック


私は、これはあなたを助けることができると思います。

関連する問題