users@glassfish.java.net

Re: JPA/Toplink? question related to _at_Transient fields

From: <glassfish_at_javadesktop.org>
Date: Wed, 14 May 2008 11:49:55 PDT

Well, this doesn't really qualify as an [i]answer[/i], but this is kind of interesting.

I seem to recall back in the mists of time discussions on this forum about what the difference is between persist() and merge() (on EntityManager).

Some folks were saying at the time that there really [i]was[/i] no difference. I wasn't particularly vocal but I fell into this camp--you could take an entity and merge() it, and if the EntityManager had never seen it before, then, well, it seemed to do something like an implicit persist() first, and [i]then[/i] the merge.

Except of course it doesn't.

Perhaps this will be helpful only to me, but in case it might be helpful to others, here's how I think about it.

persist() takes your object, whatever it looks like, and shoves it in the EntityManager's pool of managed entities. That includes any @Transient but non-transient state. If you then later on come back and merge() an object whose identifier tells the EM that indeed the object is part of the pool, a true merge() takes place and no state--persistent, non-_at_Transient or otherwise--is lost.

If instead you let merge() do the "hmm, have I seen this before?" logic, and if you hand it a brand new, never-before-persisted object, then you are effectively telling the JPA provider, "Please create a new instance of this object and merge [i]this[/i] object's state into [i]its[/i] state."

That's what burned me. For whatever reason, merge()-in-the-presence-of-a-brand-new-object does [i]not[/i] do an implicit persist(), but simply does something I'm sure similar to yourObject.getClass().newInstance(), and then copies your object's [i]persistent state only![/i] on top of that new instance.

There is doubtless some theoretical constraint that makes this weird behavior necessary--I can't for the life of me think what it might be, but there you go. Anyhow, to [i]avoid[/i] this behavior, make sure for a generic "save method" implementation you do something like this:
[code]
if (em.contains(yourObject)) {
  returnValue = em.merge(yourObject);
} else {
  em.persist(yourObject);
  returnValue = yourObject;
}
[/code]

I'd still be interested to know why merge() explicitly creates a new instance to manage instead of deliberately doing an internal persist() first in the case of a never-before-managed object.

Thanks,
Laird
[Message sent by forum member 'ljnelson' (ljnelson)]

http://forums.java.net/jive/thread.jspa?messageID=274365