次のエンティティ クラスと双方向の 1 対多の関係があります。
0 または 1 クライアント <-> 0 以上の製品注文
クライアントエンティティを永続化する場合、関連する製品注文エンティティも永続化する必要があります (「親」クライアントへの外部キーが更新されている可能性があるため)。
もちろん、必要な CASCADE オプションはすべてクライアント側で設定されます。ただし、次のシナリオのように、既存の製品注文を参照しながら、新しく作成されたクライアントが初めて保持される場合は機能しません。
- 製品注文 '1' が作成され、保持されます。正常に動作します。
- クライアント '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 を使い続けたいです (ベンダー固有のソリューションはありません)。