一对多关系:使用JPA 2.0更新删除的子项 [英] One-to-many relationship: Update removed children with JPA 2.0

查看:176
本文介绍了一对多关系:使用JPA 2.0更新删除的子项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有双向的一对多关系。

I have a bidirectional one-to-many relationship.

0或1 客户端< - > 0或更多的列表产品订单

0 or 1 client <-> List of 0 or more product orders.

应在两个实体上设置或取消设置该关系:
在客户端,我想设置列表分配给客户的产品订单;然后应该将客户端设置/取消设置为自动选择的订单。
在产品订单方面,我想设置分配了oder的客户端;然后应该从之前被分配的客户列表中删除该产品订单,并将其添加到新分配的客户列表中。

That relationship should be set or unset on both entities: On the client side, I want to set the List of product orders assigned to the client; the client should then be set / unset to the orders chosen automatically. On the product order side, I want to set the client to which the oder is assigned; that product order should then be removed from its previously assiged client's list and added to the new assigned client's list.

我想使用纯JPA 2.0注释和一个合并仅调用实体管理器(使用级联选项)。我已尝试使用以下代码片段,但它不起作用(我使用EclipseLink 2.2.0作为持久性提供程序)

I want to use pure JPA 2.0 annotations and one "merge" call to the entity manager only (with cascade options). I've tried with the following code pieces, but it doesn't work (I use EclipseLink 2.2.0 as persistence provider)

@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
}

持久客户端的门面代码:

Facade code for persisting client:

// call setters on entity by JSF frontend...
getEntityManager().merge(client)

持久产品订单的外观代码:

Facade code for persisting product order:

// call setters on entity by JSF frontend...
getEntityManager().merge(productOrder)

在订单一侧更改客户端分配时,它运作良好:在客户端,订单从以前的客户列表中删除并被添加到新客户列表中(如果已重新分配)。

When changing the client assignment on the order side, it works well: On the client side, the order gets removed from the previous client's list and is added to the new client's list (if re-assigned).

在客户端更改时,我只能添加订单 (在订单方面,执行了对新客户端的分配),但是当我从客户端列表中删除订单时它会忽略(在保存和刷新之后,它们仍然在客户端的列表中,并且在订单si上de,他们仍然被分配给以前的客户。

BUT when changing on the client side, I can only add orders (on the order side, assignment to the new client is performed), but it just ignores when I remove orders from the client's list (after saving and refreshing, they are still in the list on the client side, and on the order side, they are also still assigned to the previous client.

为了澄清,我不想使用删除孤儿选项 :从列表中删除订单时,不应从数据库中删除订单,但应更新其客户端分配(即,为null),如Client#setOrders方法中所定义。如何实现这一目标?

Just to clarify, I DO NOT want to use a "delete orphan" option: When removing an order from the list, it should not be deleted from the database, but its client assignment should be updated (that is, to null), as defined in the Client#setOrders method. How can this be archieved?

编辑:感谢我在这里收到的帮助,我能够解决这个问题。请参阅下面的解决方案:

EDIT: Thanks to the help I received here, I was able to fix this problem. See my solution below:

客户端(一个/拥有方)存储在临时字段中修改的订单。

The client ("One" / "owned" side) stores the orders that have been modified in a temporary field.

@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;
}

门面上,当持久化时,会检查如果有任何实体也必须持久化。请注意,我使用了一个接口来封装这个逻辑,因为我的外观实际上是通用的。

On the facade, when persisting, it checks if there are any entities that must be persisted, too. Note that I used an interface to encapsulate this logic as my facade is actually generic.

// 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);
    }
}


推荐答案

JPA不这样做,据我所知,没有JPA实现也这样做。 JPA要求您管理关系的两个方面。当关系中只有一方被更新时,这有时被称为对象损坏

JPA does not do this and as far as I know there is no JPA implementation that does this either. JPA requires you to manage both sides of the relationship. When only one side of the relationship is updated this is sometimes referred to as "object corruption"

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

JPA does define an "owning" side in a two-way relationship (for a OneToMany this is the side that does NOT have the mappedBy annotation) which it uses to resolve a conflict when persisting to the database (there is only one representation of this relationship in the database compared to the two in memory so a resolution must be made). This is why changes to the ProductOrder class are realized but not changes to the Client class.

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

Even with the "owning" relationship you should always update both sides. This often leads people to relying on only updating one side and they get in trouble when they turn on the second-level cache. In JPA the conflicts mentioned above are only resolved when an object is persisted and reloaded from the database. Once the 2nd level cache is turned on that may be several transactions down the road and in the meantime you'll be dealing with a corrupted object.

这篇关于一对多关系:使用JPA 2.0更新删除的子项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆