On Mon, Jan 20, 2014 at 10:57 AM, Linda DeMichiel <
linda.demichiel_at_oracle.com> wrote:
> What's the use case you are trying to address?
>
The use case is detachment, it seems. There's some centralized code in our
application (I'm not the author, so reading the tea leaves here) that
processes various objects handed to it. Some of these things are entities;
some are not. The code I'm looking at checks to see if the objects it has
been handed are entities—it does this in a naive, borked manner. If this
test passes, then the code calls
entityManager.detach(theObjectInQuestion)so that consumers do not
inadvertently modify the object and thus
inadvertently cause a write to happen at flush() time (something we've been
plagued with; educating developers about the nuances of the persistence
context and the lifecycle of an entity proves to be difficult in the real
world; there doesn't seem to be a vendor-independent way to mark bulk piles
of objects coming back from a query as being read-only or detached or
otherwise not-in-the-persistence-context, which turns out to be a case we
seem to need quite often—but I might be missing something).
We are also trying to keep our persistence contexts free of as many
unnecessary entities as possible if we know at load time that we're not
going to want them sent back "down" to the database at flush() time under
any circumstances; it looks like that is another reason for the code I'm
staring at. So: detaching anything that is just throwaway so that the
persistence context isn't clogged up with entities we have no interest in
having tracked by the EntityManager.
Obviously we can't call detach(theObject) blindly either, as that too would
roll the transaction back.
The naive and wholly unsuitable way that the current code checks for
"entityness" at the moment is to see if the class in question has the
@Entity annotation applied to it. Obviously this ain't gonna cut it for
many thousands of reasons.
It seems that if perhaps I move the dumb test that I found from:
final Class<?> clazz = object.getClass();
return clazz != null && clazz.getAnnotation(Entity.class) != null; // XXX
yuck borked ugly
...to something more like (typed off the cuff):
final Metamodel mm = em.getMetamodel();
assert mm != null;
boolean isEntity = false;
try {
isEntity = mm.entity(clazz) != null;
} catch (final IllegalArgumentException notAnEntity) {
// Thus speaketh
http://docs.oracle.com/javaee/6/api/javax/persistence/metamodel/Metamodel.html#entity(java.lang.Class)
//
// The JPA specification does not indicate that a RuntimeException
// thrown by a Metamodel operation will roll the transaction back,
// so the transaction should still be committable at this point.
isEntity = false;
}
return isEntity && em.contains(object);
...that might do it.
In general, we have several cases where we have an object in our hand and
no good (convenient, non-cumbersome,
non-pedantic-slog-through-Metamodel-return-values)
way to determine if that object "belongs to" the current (whatever that
might mean) EntityManager (we use several attached to various data sources).
Perhaps that's a fault in our code, but I'd hazard it's a common fault in
JPA development in the real world everywhere. IMHO IWBNI this API were
simpler and more resilient.
Best,
Laird
--
http://about.me/lairdnelson