EJB3: em.merge()
Hi,
I have a detached entity that I return to the application server
as a parameter. Before being able to work with this entity on the
server, I have to merge it, right?
Everywhere I look this is done simply by
@PersistenceContext
private EntityManager em;
em.merge(entity);
entity is now supposed to be attached to the database again.
But with me, this doesn't work! What I have to do is this:
@PersistenceContext
private EntityManager em;
entity = em.merge(entity);
Can someone confirm on this? I think I read this somewhere,
too, but can't remember where. JavaDoc also says that the
merge method has a return value:
"Returns: the instance that the state was merged to"
Thanks for clarification
-Danny
[900 byte] By [
dan-ba] at [2007-10-2 22:21:05]

The spec says that
If X is a detached entity, the state of X is copies onto a pre-existing managed entity instance X' of the same identity or a new managed copy X' of X is created.
So the original instance X that was detatched remains unchanged by the call to em.merge(X). The managed instance X' that was updated by the call to em.merge(X) is the instance that was returned.
Managed instances may be a different class instance or may have had weaving or other symantics applied to them, which is why em.merge(X) may need to give you a different instance from the one you passed to it (i.e. this is a decision that allows implementers of EJB3 Persistence the flexibility to implement the functionality in different ways)
so yes, you need to do something like
entity = em.merge(entity);
in order for the instance variable entity to now point to the managed entity.
NB this should be highlighting the issue of what could happen if multiple instances in your VM reference entity... after doing entity = em.merge(entity) you may need to update all their references too or you could end up with a big mess
> For the reference thing: I take care of that by using
> a cascading merge.
Noooo, that's not what I'm getting at.... an example for you:
@Entity
public class Parent {
private List<Child> children;
...
}
@Entity
public class Child {
private Parent parent;
...
}
public class Client {
@PersistenceContext
EntityManager em;
...
void someMethod() {
...
Parent p;
p = em.find(Parent.class, mykey);
em.detatch(p);
...
Child c = p.getChildren().get(0);
...
p = em.merge(p);
...
assert c.getParent() != p;
...
}
}
The call to em.merge will merge all the children with your cascade... but any direct references that you have to the children will _still_ be the references to the detached entities.
Uh! Okay...
I thought, that the container will take care of the references itself! I mean,
it follows the references to merge all the referenced entities so why not
also update the references itself?
Is there an effective way of updating all the references? I have 4 levels of
references here (like GreadGrandFather, GrandFather, Father, Son). The
client gives me the GreatGrandFather. Looks like a pretty expensive method
that I need (4 level loop?). Please say "Nooooo!" this time again :-)
I really appreciate your help! Thank you very much!
Well the solution is to re-navigate from the managed entity...
greatGrandFather = em.merge(greatGrandFather);
gives you a managed entity, as long as you only work starting from the managed entity, it will only give you references to managed objects...
the problem I was referring to is if you, somewhere else, have kept a reference to a Son instance, that reference does not link to the instance that greatGrandFather points to at all.
Hope this helps