私は双方向の一対多の関係を持っています。
0 または 1クライアント<-> 0 個以上の製品注文のリスト。
その関係は、両方のエンティティで設定または設定解除する必要があります。クライアント側では、クライアントに割り当てられた製品注文のリストを設定したいと考えています。クライアントは、自動的に選択された注文に設定/設定解除する必要があります。製品注文側では、注文が割り当てられているクライアントを設定したい。その製品注文は、以前に割り当てられたクライアントのリストから削除され、新しい割り当てられたクライアントのリストに追加されます。
純粋な JPA 2.0 アノテーションと、エンティティ マネージャーへの "マージ" 呼び出しのみを使用したい (カスケード オプションを使用)。次のコードを試してみましたが、うまくいきません (永続化プロバイダーとして EclipseLink 2.2.0 を使用しています)。
@Entity
public class Client implements Serializable {
@OneToMany(mappedBy = "client", cascade= CascadeType.ALL)
private List<ProductOrder> orders = new ArrayList<>();
public void setOrders(List<ProductOrder> orders) {
for (ProductOrder order : this.orders) {
order.unsetClient();
// don't use order.setClient(null);
// (ConcurrentModificationEx on array)
// TODO doesn't work!
}
for (ProductOrder order : orders) {
order.setClient(this);
}
this.orders = orders;
}
// other fields / getters / setters
}
@Entity
public class ProductOrder implements Serializable {
@ManyToOne(cascade= CascadeType.ALL)
private Client client;
public void setClient(Client client) {
// remove from previous client
if (this.client != null) {
this.client.getOrders().remove(this);
}
this.client = client;
// add to new client
if (client != null && !client.getOrders().contains(this)) {
client.getOrders().add(this);
}
}
public void unsetClient() {
client = null;
}
// other fields / getters / setters
}
クライアントを永続化するためのファサード コード:
// call setters on entity by JSF frontend...
getEntityManager().merge(client)
製品注文を永続化するためのファサード コード:
// call setters on entity by JSF frontend...
getEntityManager().merge(productOrder)
注文側でクライアントの割り当てを変更すると、うまく機能します。クライアント側では、注文が以前のクライアントのリストから削除され、新しいクライアントのリストに追加されます (再割り当てされた場合)。
しかし、クライアント側で変更する場合、注文を追加することしかできません(注文側では、新しいクライアントへの割り当てが実行されます)が、クライアントのリストから注文を削除すると無視されます(保存して更新した後、それらはまだ残っていますクライアント側のリスト、および注文側でも、以前のクライアントに割り当てられたままです。
明確にするために、「delete orphan」オプションを使用したくありません。リストから注文を削除する場合、データベースから削除するべきではありませんが、クライアントの割り当てを更新する必要があります (つまり、null に)。 Client#setOrders メソッドで定義されています。これはどのようにアーカイブできますか?
編集:ここで受けた助けのおかげで、この問題を解決できました。以下の私の解決策を参照してください。
クライアント ( 「One」/「所有」側) は、変更された注文を一時フィールドに保存します。
@Entity
public class Client implements Serializable, EntityContainer {
@OneToMany(mappedBy = "client", cascade= CascadeType.ALL)
private List<ProductOrder> orders = new ArrayList<>();
@Transient
private List<ProductOrder> modifiedOrders = new ArrayList<>();
public void setOrders(List<ProductOrder> orders) {
if (orders == null) {
orders = new ArrayList<>();
}
modifiedOrders = new ArrayList<>();
for (ProductOrder order : this.orders) {
order.unsetClient();
modifiedOrders.add(order);
// don't use order.setClient(null);
// (ConcurrentModificationEx on array)
}
for (ProductOrder order : orders) {
order.setClient(this);
modifiedOrders.add(order);
}
this.orders = orders;
}
@Override // defined by my EntityContainer interface
public List getContainedEntities() {
return modifiedOrders;
}
ファサードでは、永続化するときに、永続化しなければならないエンティティがあるかどうかもチェックします。私のファサードは実際には汎用的であるため、インターフェースを使用してこのロジックをカプセル化したことに注意してください。
// call setters on entity by JSF frontend...
getEntityManager().merge(entity);
if (entity instanceof EntityContainer) {
EntityContainer entityContainer = (EntityContainer) entity;
for (Object childEntity : entityContainer.getContainedEntities()) {
getEntityManager().merge(childEntity);
}
}