Two reasons to prefer Hibernate JPA over EclipseLink on GlassFish

EclipseLink is the default JPA provider on the GlassFish JEE server. As such, I figured EclipseLink would work well as the persistence provider. However, we began a recent JEE 6 project using GlassFish v3 and chose Hibernate as the JPA provider because of the team's familiarity with Hibernate 3. We later switched to EclipseLink to be compatible with another application running on the server -- and immediately encountered two annoying problems that never occurred with Hibernate. This article is meant to document those EclipseLink problems in case it helps others with similar EclipseLink issues on GlassFish.

The first problem we saw was a sporadic ClassCastException after a module redeploy. The second problem we found was that entity methods marked with @PrePersist were not being called when the entity was being saved to the database. Instead, those fields would remain null when EclipseLink executed the INSERT SQL statement, resulting in constraint violations for our non-nullable columns.

The ClassCastException occurs at a line in our code that assigns an entity to a reference variable declared as the type of its parent class, which is a JPA mapped superclass. A widening cast like that should cause no problem, so this was a head-scratcher. Adding to the mystery is we had no problem with this code when using Hibernate JPA. The entity class in question, ActionTypeLookup, as shown in the stack trace below, directly extends the parent class type that it is being assigned to.

Here are the partial class definitions. The parent class type, CodeLookupValues,
public abstract class CodeLookupValues implements Serializable {
is a @MappedSuperclass that the entity ActionTypeLookup directly extends:
public class ActionTypeLookup extends CodeLookupValues implements Serializable {
The persistence code is defined in a module (an EAR file) containing the JPA persistence unit definition. The ClassCastException occurs only if that same module is unloaded and reloaded from the server several times. That is, something you do a lot of during development. We had loaded and unloaded this EAR file many times on GlassFish with no problem when using Hibernate. Shortly after the switch to EclipseLink, we would see this stack trace in the log at deploy time: Exception while loading the app
javax.ejb.EJBException: javax.ejb.CreateException: Initialization failed for Singleton LookupSessionFacade
at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(
at com.sun.ejb.containers.AbstractSingletonContainer.instantiateSingletonInstance(
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(
at org.glassfish.ejb.startup.SingletonLifeCycleManager.doStartup(
at org.glassfish.ejb.startup.EjbApplication.start(
... [removed several classes involved in installing the EAR] ...
at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(
at com.sun.grizzly.util.AbstractThreadPool$
Caused by: javax.ejb.CreateException: Initialization failed for Singleton LookupSessionFacade
at com.sun.ejb.containers.AbstractSingletonContainer.createSingletonEJB(
at com.sun.ejb.containers.AbstractSingletonContainer.access$100(
at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(
... 36 more
Caused by: java.lang.ClassCastException: my.customer.package.shared.datamodel.reference.ActionTypeLookup cannot be cast to my.customer.package.shared.datamodel.reference.CodeLookupValues
at my.customer.package.shared.datamodel.persistence.LookupSessionFacadeBean.reloadCommonLookupValues(
at my.customer.package.shared.datamodel.persistence.LookupSessionFacadeBean.readLookupTables(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(
at sun.reflect.DelegatingMethodAccessorImpl.invoke(
at java.lang.reflect.Method.invoke(
at com.sun.ejb.containers.interceptors.BeanCallbackInterceptor.intercept(
... [removed several more classes involved in installing the EAR] ...
at com.sun.ejb.containers.interceptors.CallbackChainImpl.invokeNext(
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(
at com.sun.ejb.containers.AbstractSingletonContainer.createSingletonEJB(
... 38 more
We see the error at deploy time because the application has a @Singleton EJB that also is annotated with @Startup so the server loads it at deploy time. I don't know if having a non-startup EJB or a non-singleton EJB would make a difference.

It turns out this was the easiest problem to work around -- the ClassCastException goes away if you restart the domain. But I never discovered what causes the exception. At first, I thought the problem must be caused by the two classes being loaded by separate classloaders due to the way we had structured the modules in our application. However, both classes are:
  • Deployed in the same Java package
  • Inside the same JAR
  • Packaged and deployed inside the same EAR.
The ClassCastException does not occur all the time. It just suddenly pops up after a series of deploy/undeploy cycles and does not go away until we restart the GlassFish domain.

My best theory so far as to what causes the ClassCastException is it is a bug in GlassFish's OSGi bundle class loading. The problem appears as if some GlassFish classloader has the parent MappedSuperclass class loaded even after module undeploy but not the child class. Subsequently, when the updated EAR file is deployed again, the new module's newly assigned Archive classloader sees that the parent class is already loaded but needs to load the child classes from the new EAR files's JAR. When the code tries a widening assignment of a child entity to a reference type of the parent class -- kaboom! -- a ClassCastException because the two classes are now unrelated because of the different classloaders.

The GlassFish OSGi implementation gets my suspicion only because EclipseLink is deployed in GlassFish as an OSGi bundle, and the ClassCastException never occurred when using Hibernate, which is deployed as a set of jar files inside the server's shared library classpath.

Better theories are welcome in the comments. I poked around but found no others reporting the same bug or that this problem has been fixed in a GlassFish update (we are not running the latest updates of v3). This ClassCastException is only an annoyance because the workaround is to restart the GlassFish domain/server. A restart always fixes the problem and it goes away, until after another series of deploy/undeploy cycles.

@PrePersist problem

The second and more vexing problem is with the way EclipseLink synchronizes its entity session cache with the database. If I understand the problem correctly from reading various forum postings, if you instantiate a new entity object and call an EntityManager.find() method before the new, detached entity has been persisted with EntityManager.persist(), EclipseLink synchronizes all the detached entity states with the database before performing the query. That synchronization makes sense: the database is going to need to know about the new rows in the tables if the SELECT query is going to find the correct data.

The problem, at least for me, is that when EclipseLink performs the INSERT statement to persist the unsaved entities, it does not call the @PrePersist methods on the entity. I was counting on @PrePersist methods to set values like created-date and last-updated-date, which cannot be null in our schema. OK, I can understand that EclipseLink opted not to call @PrePersist methods when it is merely inserting the data for its convenience and not because EntityManager.persist() was called. But the unexpected behavior required some logic change in our entity code because Hibernate always seemed to call our @PrePersist methods before performing an INSERT on the database. After the switch to EclipseLink, we started seeing database constraint violation errors on these @PrePersist columns.

This second problem with EclipseLink, then, cannot be called a bug, but it was unexpected behavior worth documenting here. I assumed incorrectly that @PrePersist methods, by definition, always would be called before an entity got persisted. Wrong.

Those are the only two unusual problems we saw after switching from Hibernate to EclipseLink. The other problems that occurred were some HQL-specific queries failed, but those could all be converted to standard JPQL with a little alteration.

After complaining about EclipseLink issues, I will profess one preference for EclipseLink: The debugging log messages and error log messages seemed clearer and more straightforward with EclipseLink. When one our our HQL queries failed or I needed to trace what queries were being called and when, EclipseLink's debugging messages just seemed clearer and easier to understand than many of Hibernate's logging messages. I could see queries more clearly being translated from JPQL to Oracle SQL, and see what values were being bound to query substitution parameters. I do hand it to EclipseLink for making its database interactions more transparent than I am used to with Hibernate. So when we had the @PrePersist problem, at least I was able to debug and diagnose them fairly easily.

Astrogeeks and photographers: Look eastward this weekend

Moonrise over D.C. in winter Moonrise on Feb. 28, 2010

The full moon will rise this weekend at nearly 90 degrees azimuth for those in Washington, D.C. That means the moon will be almost directly to the east. Since the National Mall and many of its famous monuments and buildings align along an east-west axis, the astronomical phenomenon promises stunning moonrises from places like the Washington Monument, the Lincoln Memorial, and the Netherlands Carillon as the moon slowly rises behind or next to the U.S. Capitol. If the expected clouds abate on Friday and the weather holds out, that is.

As you can see from these photos of near-90 azimuth moonrises last year, the moon looks great near the horizon when looking east across the Mall. Here are the stats for the weekend:

On Friday, the nearly-full moon will rise in D.C. at 6:23 p.m. EDT at 89 degrees azimuth (source). On Saturday, the full moon rises at 7:39 p.m. at 97 degrees azimuth.

And if the full moon due east isn't enough to pique your geeky astronomical interest, this weekend's moon will be a big and bright moon. Saturday, the full moon is at perigee -- its closest approach to earth. This perigee is the closest the moon will get to the Earth for all of 2011: 221,575 miles (356,575 kilometers). That's 31,064 miles closer than the moon was on March 6, according to EarthSky. EarthSky says Saturday's full moon will be its closest encounter with the Earth since Dec. 12, 2008, and the closest it will be until Nov. 14, 2016.

For photographers interested in capturing the event, outside the Lincoln Memorial and the Netherlands Carillon should be good places to set up your tripod. (Tip: You might want to get to the Carillon early to stake out an unobstructed spot for your tripod. It's a popular place for full moon photos. Note that the path in front of the bronze lions is a popular one with joggers, bikers and pedestrians, so get your siteline set up with that in mind.) As far as weather goes, the current forecast calls for partly cloudy Friday around moonrise with a slight chance of rain. But sometimes a low cloud layer can make for great photos if the clouds aren't dense. Saturday also promises to be partly cloudy with some rain possible in the morning. But the clouds are supposed to clear by moonrise. Saturday promises to be the better day for weather but with the 97 degrees azimuth, perhaps less-stunning photos (more like the one below).
Moonrise over D.C. on Aug. 25, 2010 Moonrise on Aug. 25, 2010
I'm looking forward to some beautiful moonrises this weekend. Share the moment with someone you love -- but hey, take the camera. If you grab some good photos, please send me a link in the comments.