Posted by ss141213
on May 25, 2009 at 2:01 AM PDT
Recently a GlassFish user while using Hibernate JPA provider asked how they can access the underlying Hibernate Session object. Yes, EnityManager.getDelegate() is the way to obtain such information, but let's see if it is really that straight forward.
Recently a GlassFish user, who is using Hibernate JPA provider, asked how they can access the underlying Hibernate Session object from the injected EntityManager object. My answer was to use:
org.hibernate.Session session = ((org.hibernate.ejb.EntityManagerImpl) em.getDelegate()).getSession();
If it is that simple, why am I making an issue out of it? It is a highly non-portable API and you really have to write different code in different application servers even while using the same persistence provider. Well, read on to find out why...
Look at the javadocs of getDelegate() and tell me what it returns. The javadoc says it returns "the underlying provider object for the EntityManager." What does that mean? That's the subject of discussion.
In enterprise platform, when you look up and EntityManager in JNDI or when your component is injected with an EntityManager (by using @PersistenceContext), you really don't get the actual EntityManagerImpl of the provider. What you typically get is a wrapper which acts a frontend to the underlying object. The reasons for having such a wrapper are manifold. e.g., the wrapper is designed as a Serializable object so that it can be bound in JNDI, where as the provider's implementation may not be. More importantly, it's the wrapper which implements the EE semantics like persistence context propagation . If you are interested in looking at how such a Wrapper looks like, take a look at this GlassFish Wrapper implementation . As you can see, in GlassFish, getDelegate() returns the underlying provider's EntityManager implementation. So, if you are using Hibernate in GlassFish, then getDelegate() returns org.hibernate.ejb.EntityManagerImpl
As a result,
returns Hibernate Session object in GlassFish.
But, the above code would not work in JBoss. They suggest the following:
org.hibernate.Session session = (Session)manager.getDelegate();
It is difficult to say which implementation is correct, when the spec, in the javadoc of getDelegate, calls out the non-portability of the API like this:
"The result of this method is implementation specific."
Lack of clarity of the spec leads to pain for programmers. You think you have written to JPA spec and your code is portable as long as you are using Hibernate JPA provider, but it is not. It now varies from one application server to another. I would really love to know what WebLogic and WebSphere return for the above method, but that's a separate point. Actually, once a user filed the following bug in GlassFish:
https://glassfish.dev.java.net/issues/show_bug.cgi?id=2923 , but we could not fix it because we think there is an advantage of the current implementation. Since we can't really say one of the two implementations is at fault, let's analyse the pros and cons of the two implementations.
From a design pattern point of view, the wrapper should call underlying object's method. So, this mean, EntityManagerWrapper.getDelegate() should call delegate.getDelegate(). It is a more logical choice. I guess this is what happens in JBoss, but in GlassFish, we return the delegate itself. But, returning the delegate in getDelegate() (just like what GlassFish does) means user now have access to to a much richer set of APIs that what is available via javax.persistence.EntityManager. One such extra API could be to get the Session object. So, I am inclined to favour GlassFish's implementation.
I really wish this is something that gets addressed in the upcoming JPA 2.0 spec. If it has already been addressed, even better. Please don't make the programmer suffer. They don't always have the time and resource to get to the underlying problem and they end up making value judgment about implementations.