一对多关系:使用JPA 2.0更新删除的子项

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)

Pac*_*ace 5

JPA没有这样做,据我所知,没有JPA实现可以做到这一点.JPA要求您管理关系的两个方面.当只更新关系的一侧时,这有时被称为"对象损坏"

JPA确实在双向关系中定义了一个"拥有"方(对于OneToMany,这是没有mappedBy注释的一方),它用于在持久化到数据库时解决冲突(只有一个表示数据库中的关系与内存中的两个相比,因此必须做出解决方案).这就是为什么要实现对ProductOrder类的更改,而不是对Client类的更改.

即使拥有"拥有"关系,您也应该始终更新双方.这通常会导致人们只依赖于更新一方,并且当他们打开二级缓存时会遇到麻烦.在JPA中,只有当对象被持久化并从数据库重新加载时,才会解决上述冲突.一旦打开第二级缓存,可能会有几笔交易在此期间,同时您将处理损坏的对象.