One-to-Many Relationship: Updating Deleted Children with JPA 2.0

I have a one-to-many bi-directional relationship.

0 or 1 customer ↔ List of 0 or more product orders .

This relationship must be set or canceled for both objects: On the client side, I want to establish a list of product orders assigned to the client; The client must be installed / disabled for the selected orders automatically. On the product ordering side, I want to install a client who is assigned another; this product order must then be removed from its previously configured customer list and added to the new assigned customer list.

I want to use pure JPA 2.0 annotations and a single merge call only to the entity manager (with cascading options). I tried with the following code snippets but it does not work (I use EclipseLink 2.2.0 as a continuity 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 Pending Product Order:

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

When changing the destination of the client on the order side, it works well: on the client side, the order is deleted from the previous customer list and added to the new customer list (if it is redefined).

( , ), , ( , - .

, " " . , ( ), Client # setOrders. ?


EDIT: , , . . :

("" / "" ) , .

@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);
    }
}
+3
3

JPA , , JPA . JPA , . , " "

JPA "" ( OneToMany , mappedBy), ( , ). ProductOrder, Client.

"" . , . JPA , . , , .

+5

, , .

, , , , .

. , .

EclipseLink , , JPA.

0

ProductOrder, detached .

, :

@Entity public class ProductOrder implements Serializable { 
  /*...*/

  //in your case this could probably be @Transient
  private boolean detached;  

  @PreUpdate
  public void detachFromClient() {
    if(this.detached){
        client.getOrders().remove(this);
        client=null;
    }
  }
}

Instead of deleting the orders you want to delete, you set the value "Disabled" to "true". When you merge and clear the client, the object manager will detect the changed order and execute the @PreUpdate callback, effectively canceling the order from the client.

0
source

All Articles