我有一个双向一对多关系。
0或1个客户端<->0或多个产品订单列表。
该关系应在两个实体上设置或取消: 在客户端方面,我想设置分配给客户端的产品订单列表;然后应自动选择设置/取消选择的订单。 在产品订单方面,我想将客户端设置为所分配的客户端;然后应从其先前分配的客户端列表中删除该产品订单并添加到新分配客户端的列表中。
我想使用纯JPA 2.0注释和一个“合并”调用到实体管理器(具有级联选项)。我已尝试使用以下代码片段,但它不起作用(我使用EclipseLink 2.2.0作为持久性提供程序)。
在订单端更改客户分配时,它能够正常工作:在客户端上,订单将从先前客户的列表中移除,并添加到新客户的列表中(如果重新分配)。
但是,在客户端更改时,我只能添加订单(在订单端,分配给新客户),但当我从客户列表中删除订单时,它会被忽略(保存并刷新后,订单仍然在客户端的列表中,在订单端它们仍然分配给先前的客户)。
为了澄清,我不想使用“删除孤儿”选项:从列表中删除订单时,它不应从数据库中删除,而其客户分配应更新(即为空),如Client#setOrders方法中所定义。怎样才能实现这一点?
编辑:由于我在这里得到的帮助,我成功解决了这个问题。请看下面的解决方案:
客户端(“One” /“ Owned”端)在一个临时字段中存储已修改的订单。
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)
在订单端更改客户分配时,它能够正常工作:在客户端上,订单将从先前客户的列表中移除,并添加到新客户的列表中(如果重新分配)。
但是,在客户端更改时,我只能添加订单(在订单端,分配给新客户),但当我从客户列表中删除订单时,它会被忽略(保存并刷新后,订单仍然在客户端的列表中,在订单端它们仍然分配给先前的客户)。
为了澄清,我不想使用“删除孤儿”选项:从列表中删除订单时,它不应从数据库中删除,而其客户分配应更新(即为空),如Client#setOrders方法中所定义。怎样才能实现这一点?
编辑:由于我在这里得到的帮助,我成功解决了这个问题。请看下面的解决方案:
客户端(“One” /“ Owned”端)在一个临时字段中存储已修改的订单。
@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);
}
}