views:

170

answers:

1

Why can i remove elements of a bidirectional relation although only one side of the relation is managed in persistence context (Example I)? When i have an unidirectional Relationship that doesn't work (see Example II). Why?

Entities:

@Entity
Class User {
    ...
    @OneToMany(mappedBy = "user")
    private List<Process> processes;

    @OneToOne // Unidirectional
    private C c;
    ...

    @PreRemove
    private void preRemove() {
        for (Process p : processes) {
            p.internalSetUser(null);
        }
    }
   ...
}

@Entity
Class Process {
    ...
    @ManyToOne
    private User user;
    ...

    @PreRemove
    protected void preRemove() {
        if (this.user != null) {
            user.internalRemoveProcess(this);
        }
    }
   ...
}

@Entity
Class C {

 }

Example I:

// Create User u1 with Processes p1, p2

tx.start();
// Only u1 is manged in persistence context and no process
userFacade.delete(u1); // There following is called: >> em.remove(em.merge(u1)); // Works
tx.commit();

Example II:

// Create User u and Object C c, establish their relation.

tx.start();
cFacade.remove(c); //>>MySQLIntegrityConstraintViolationException,foreign key constraint fails
ty.commit();

In the first example i use these internal methods to set in each case the other side of the relation but this other side is not managed in persistence context i think?! When i change a process of a user and save the user, the process is not updated unless i uses cascade.MERGE or if both are loaded in a transaction and therefor are managed in pc. So why does the removing work?

+1  A: 

In Example II, I guess you would have to call user.setC(null) before deleting the c.

In Example I, here is my understanding. You are first merging u1 so a u1' gets loaded into the PC and the state of u1 is copied to u1' (and that's all since you're not cascading MERGE), which is then returned. Then you call remove (on u1'), the preRemove gets called and changes p1' and p2'. They are thus dirty and will get updated appropriately on flush (setting the FK to NULL), while u1' will be deleted. And everything works.

Just in case, here are the semantics of the merge operation from the JPA 2.0 specification:

3.2.7.1 Merging Detached Entity State

The merge operation allows for the propagation of state from detached entities onto persistent entities managed by the entity manager.

The semantics of the merge operation applied to an entity X are as follows:

  • If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X' of the same identity or a new managed copy X' of X is created.
  • If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity instance X'.
  • If X is a removed entity instance, an IllegalArgumentException will be thrown by the merge operation (or the transaction commit will fail).
  • If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by relationships from X if these relationships have been annotated with the cascade element value cascade=MERGE or cascade=ALL annotation.
  • For all entities Y referenced by relationships from X having the cascade element value cascade=MERGE or cascade=ALL, Y is merged recursively as Y'. For all such Y referenced by X, X' is set to reference Y'. (Note that if X is managed then X is the same object as X'.)
  • If X is an entity merged to X', with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then navigation of the same association from X' yields a reference to a managed object Y' with the same persistent identity as Y.

The persistence provider must not merge fields marked LAZY that have not been fetched: it must ignore such fields when merging.

Any Version columns used by the entity must be checked by the persistence runtime implementation during the merge operation and/or at flush or commit time. In the absence of Version columns there is no additional version checking done by the persistence provider runtime during the merge operation.

Reference

  • JPA 2.0 Specification
    • 3.2.7.1 Merging Detached Entity State
Pascal Thivent
Thanks Pascal! I think in both cases you're right.For Example I its the last point of the above spec.
Michael W.
If i use for the Example I not merge but the following:**"User u1Managed = em.find(u1.getId()); em.remove(u1Managed);"** Then it also works.That means the find operation yields also to a managed p1' and p2'?
Michael W.
@Michael The `processes` are lazy but I think `p1` and `p2` gets loaded in your `PreRemove` (and become thus indeed managed).
Pascal Thivent
Yeah thats possible!
Michael W.