Spu*_*ick 3 java entity-relationship jpa one-to-many jpa-2.0
我有一个双向的一对多关系.
0或1 客户 < - > 0个或更多产品订单的列表.
应该在两个实体上设置或取消设置该关系:在客户端,我想设置分配给客户端的产品订单列表; 然后应该将客户端设置/取消设置为自动选择的订单.在产品订单方面,我想设置分配了oder的客户端; 然后,应从其先前已分配的客户列表中删除该产品订单,并将其添加到新分配的客户列表中.
我想使用纯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
}
Run Code Online (Sandbox Code Playgroud)
持久客户端的外观代码:
// call setters on entity by JSF frontend...
getEntityManager().merge(client)
Run Code Online (Sandbox Code Playgroud)
用于持久化产品订单的外观代码:
// call setters on entity by JSF frontend...
getEntityManager().merge(productOrder)
Run Code Online (Sandbox Code Playgroud)
在订单端更改客户端分配时,它运行良好:在客户端,订单从先前客户端列表中删除,并添加到新客户端列表(如果重新分配).
但是当在客户端进行更改时,我只能添加订单(在订单一侧,执行对新客户端的分配),但是当我从客户列表中删除订单时它会忽略(在保存和刷新之后,它们仍然在客户端列表,在订单端,它们仍然分配给以前的客户端.
只是为了澄清,我不想使用"删除孤立"选项:从列表中删除订单时,不应从数据库中删除它,但应更新其客户端分配(即,为null),如在Client#setOrders方法中定义.怎么能这样做?
编辑:感谢我在这里收到的帮助,我能够解决这个问题.请参阅下面的解决方案
客户端("一个"/"拥有"一方)存储已在临时字段中修改的订单.
@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;
}
Run Code Online (Sandbox Code Playgroud)
在外观上,当持久化时,它会检查是否有任何必须持久化的实体.请注意,我使用了一个接口来封装这个逻辑,因为我的外观实际上是通用的.
// 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);
}
}
Run Code Online (Sandbox Code Playgroud)
JPA没有这样做,据我所知,没有JPA实现可以做到这一点.JPA要求您管理关系的两个方面.当只更新关系的一侧时,这有时被称为"对象损坏"
JPA确实在双向关系中定义了一个"拥有"方(对于OneToMany,这是没有mappedBy注释的一方),它用于在持久化到数据库时解决冲突(只有一个表示数据库中的关系与内存中的两个相比,因此必须做出解决方案).这就是为什么要实现对ProductOrder类的更改,而不是对Client类的更改.
即使拥有"拥有"关系,您也应该始终更新双方.这通常会导致人们只依赖于更新一方,并且当他们打开二级缓存时会遇到麻烦.在JPA中,只有当对象被持久化并从数据库重新加载时,才会解决上述冲突.一旦打开第二级缓存,可能会有几笔交易在此期间,同时您将处理损坏的对象.
| 归档时间: |
|
| 查看次数: |
16969 次 |
| 最近记录: |