Evaluating EclipseLink 1.1

As I’m using the ubiquitous Hibernate 3.3 as the JPA 1.0 provider for Spincloud, I decided to try out another one. I had tried OpenJPA (spawned from Kodo JDO) when they only supported build-time bytecode enhancement and it was a pain to make it work. It worked all right but boy what a pain. There’s now an agent to provide on-the-fly enhancement but I’ll take transparent enhancement anytime.
I’ve heard about EclipseLink before. The project started when Oracle donated the respectable TopLink project to the Eclipse foundation. If the solid reputation behind TopLink was a good enough argument for me to try it, the announcement that it will be the JPA 2.0 reference implementation convinced me that I should try it out.
My goal is to evaluate if EclipseLink is production-ready. I’m applying a complex set of evaluation criteria (joking): if it can run Spincloud then it is (I was inspired by Seifer’s interview on Infoq about Ruby VMs; when asked what was the criteria for qualifying if a Ruby VM is production ready, he answered: if it runs Rails).

I have the following JPA requirements:
– column mappings, one-to-one, one-to-many
– supports BLOB fields
– supports NamedQueries and NamedNativeQueries
– support for object cache and query cache
– deployment/operational nice to have: ease of maintaining compatibility with both EclipseLink and Hibernate in the source code and runtime. Ideally I should plug-in any JPA provider without changing a single line of code. This was not attainable as I’ll explain below.

I started by downloading the binaries. I’m using Maven to bring the jars so I’ve followed the instructions here. I only changed the version since I wanted to use v1.1.0:

  <dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>1.1.0</version>
    <scope>provided</scope>
  </dependency>

There’s a single jar file called eclipselink-1.1.0.jar downloaded which is a nice change from the multitude of Hibernate jars I was accustomed with.

Next, I created a new persistence.xml file since I didn’t know how different it would be from the one tied to Hibernate. Initially I just changed the JPA provider to:

  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

as the documentation stated.
Then I had a look at my current NamedQueries and NamedNativeQueries that looked like this:

@org.hibernate.annotations.NamedQueries({
	@org.hibernate.annotations.NamedQuery(name="findStationByPK",
	        query="select station from Station station where station.id=:stationPK"
	        , cacheable=true, cacheRegion="weatherStationsRegion"),
...

and I recalled that I did this in order to make use of Hibernate’s Query cache facility, alas a non-standard way of cache enablement (not until JPA 2.0 anyway). I had to put query caching to rest and switched all my Named*Queries to standard JPA:

@javax.persistence.NamedQueries({
	@javax.persistence.NamedQuery(name="findStationByPK",
	        query="select station from Station where id=:stationPK"),
...

I’m not using the integration unit tests I have due to the issues I had with the Spring integration test framework I’m using after switching to Spring 3.0 so I’m using ad-hoc testing for now. After rebuilding/deploying the project I didn’t notice any error on Tomcat start-up so I just tried the GUI, displaying the weather in a map area. I hit the first error:

Exception [EclipseLink-7060] (Eclipse Persistence Services - 1.1.0.r3639-SNAPSHOT): 
org.eclipse.persistence.exceptions.ValidationException
Exception Description: Cannot acquire data source [java:comp/env/ds/MeteoDS].
Internal Exception: javax.naming.NamingException: This context must be accessed 
throughout a java: URL

I dug through the documentation (what’s with this documentation and what does “No dynamic weaving (instrumentation) – static weaving of entities is still available via EclipseLink” means, why JPAEclipseLinkSessionCustomizer example is broken and finally why use teh deprecated SessionCustomizer?) and found that I have to use a session customizer to make it work in Tomcat. I didn’t like this one bit (it just works in Hibernate) but I followed the instructions and EclipseLink could establish the connection with the Tomcat-configured data source as expected. This was the first duly noted turn-off.
I redeployed and browsed the weather map just to hit another problem:

  Exception Description: Syntax error parsing the query [findStationByPK: 
    select station from   Station where id=:stationPK], line 1, column 28: 
    syntax error at [where].
 Internal Exception: MismatchedTokenException(65!=66)

Hmm, no compile-time JPA query check. Not good. I checked the JPA query, it was:

  @javax.persistence.NamedQuery(name="findStationByPK",
       query="select station from Station where id=:stationPK"),
...

which looked OK but looking closely I realized that it was not. The correct syntax is (notice the station. indirection:

@javax.persistence.NamedQueries({
	@javax.persistence.NamedQuery(name="findStationByPK",
	        query="select station from Station station where station.id=:stationPK"),
	@javax.persistence.NamedQuery(name="findByStationId",
...

Hibernate was happy with the “non-standard” syntax while EclipseLink is not. No big deal, it wasn’t in many places anyway. I scanned all my queries and fixed them. Redeployed and hit this:

  Exception Description: Syntax error parsing the query [findByStationId: from
    Station station where station.stationId=:stationId], line 1, column 0: 
    unexpected token [from].

My JPA query is:

	@javax.persistence.NamedQuery(name="findByStationId",
	        query="from Station station where station.stationId=:stationId")

Again a Hibernate artifact. H3 supports the convenience of using queries that start with “from entity…” if they return entities (single or lists). The fix was simply to add “select station” in front of the JPA QL:

	@javax.persistence.NamedQuery(name="findByStationId",
	        query="select station from Station station where 
                station.stationId=:stationId")

Not a biggie either, standard is better and Hibernate can parse this kind of query. Again, rebuilt/redeployed and queried the map again. This time the error was this:

[EL Warning]: 2009-03-21 19:02:42.135--UnitOfWork(2001070194)--Exception [EclipseLink-4002] 
(Eclipse Persistence Services -
 1.1.0.r3639-SNAPSHOT): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 
You have an error in your SQL syntax; check 
the manual that corresponds to your MySQL server version for the right syntax to use 
near ':area) , LOCATION) and obs_time 
> :minSynopDateToConsiderStr UNION select distin' at line 1
Error Code: 1064
Call: select distinct STATION_FK as STATION_IDENTIFIER, LOCATION,
obs_time as REPORT_TIME  from SYNOP where Contains(GeomFromText(:area) , LOCATION) and 
obs_time > :minSynopDateToConsiderStr UNION 
select distinct STATION_FK as STATION_IDENTIFIER, 
LOCATION, REPORT_TIME as REPORT_TIME from METAR where 
Contains(GeomFromText(:area) , LOCATION) and REPORT_TIME > :minMetarDateToConsiderStr

I couldn’t find anything at first in the documentation (getting really pissed at the documentation style, can’t find anything useful) but I finally found this:

Support for the EclipseLink # convention is helpful if you are already familiar with
EclipseLink queries or if you are migrating EclipseLink queries to a JPA application.

I’m not “already familiar with EclipseLink queries” and no, it’s not helpful. So now I have to use #area instead of :area named parameter. This is really smart. Why in the world would one want to change the column with sharp? I’m starting to suspect clumsiness or plain careless coding. I still want to maintain Hibernate/EclipseLink pluggability so I have no choice but creating a static class that holds all NamedNativeQueries and parametrizes the named parameter prefix:

class NativeQueries {
public final static String WEATHER_SNAPSHOT_IN_AREA_NATIVE_QUERY ="select distinct 
STATION_FK as STATION_IDENTIFIER, LOCATION, obs_time as REPORT_TIME from SYNOP where 
Contains (GeomFromText(@named-param-prefix@area) " +
 ", LOCATION) and obs_time > @named-param-prefix@minSynopDateToConsiderStr UNION " +
"select distinct STATION_FK as STATION_IDENTIFIER, LOCATION, REPORT_TIME
as REPORT_TIME from METAR where Contains(GeomFromText(@named-param-prefix@area) " +
", LOCATION) and REPORT_TIME > @named-param-prefix@minMetarDateToConsiderStr";
...
}

Then I have to use ant to perform a pre-compilation task that replaces @named-param-prefix@ with either : or #. This really got the best of me as I’ve hit a Java compilation quirk too: you have to delete all classes that rely on the NativeQueries class since they will not recompile if the values of the public static final String values change; you have to delete all .class files and force a full compilation for them.
After I did this build change it all started working. Victory at last. Or was it? Well, no; I still have to add query caching as I heavily use spatial queries to fetch area weather (activated every time the map is dragged/zoomed). There’s the @Cache but it’s for entity caching which is good but what I also need is query caching à la Hibernate:

 @org.hibernate.annotations.NamedQuery(name="qName", query="<query>",
    cacheable=true, cacheRegion="some-cache-region")

After another round of thorough search of the documentation I found what I needed but it wasn’t what I expected. EclipseLink doesn’t support annotation-level query cache configuration. Instead you have to code-in (or use AOP to) cache writes after the queries are executed, via its ReadQuery class.
Needless to say I didn’t do it. I gave up on it right there.
On a whole this felt a lot like my (undocumented) evaluation I did over a year ago with OpenJPA (no dice too). Poor documentation (worse than EL’s) and lack of (then) runtime weaving made it a nightmare to deploy. I haven’t tried it ever since.

Prognosis: negative

This is really a shame, EclipseLink seems to be quite fast and has a footprint similar to Hibernate but:
– the documentation is awful. Guys, look at Hibernate and follow their lead. I badly need a single page HTML up-to-date documentation that I can freely browse. I can find almost anything in a matter of seconds using Hibernate’s single page HTML doc and Ctrl-F. I feel so strongly about this, I’d make an addition to the Software Craftsmanship Manifesto: “Maintain top-notch documentation”. Spring and Hibernate projects got it right and this is one of the secrets of their success. Take note.
– Use : (column) for named parameters in NamedNativeQuery; it is consistent with NamedQuery syntax (and hopefully standardized in JPA 2.0).
– Use compile-time Named*Query syntax check. It adds a great deal to predictability.
– Add the Session Customizer fix into of the base platform and make it transparent to the user. Nobody wants to write integration code to fix oddities of popular containers. “Out of the box” is valuable.
– Work on a better query caching support or push hard for a JPA 2.0 beta since there’s standardized second-level caching.

Oh and guys, some documentation pages don’t render properly on Safari, the TOCs overlap the content. Unacceptable.

Until then I’m sticking with Hibernate 3.3. It just works.

  • Yannick Majoros

    Hi,

    You were fast to write an article on Eclipselink 1.1, just released. But I think you have to work more systematically to be able to give some useful advice.

    If you remove all non-standards queries problems, there isn’t much left in your article. It seems ok to me that Eclipselink shares the jpa standard with other persistence providers, as Hibernate. Some Eclipselink extensions won’t work on hibernate, it wouldn’t be right to blame the latter.

    Other problems you mentioned were about the documentation and the weaving process.

    The documentation is complete, but I used to find it as awful as you seem to do. You just have to read a lot of thing before you understand it fully. It’s just complete.

    What about weaving? It’s just my advice, but using spring + tomcat looks to me like reinventing the wheel. What you are trying to do is adding what you miss from an application server into tomcat. Why? Using eclipselink would have taken 2 minutes on glassfish, I guess about the same on jboss. I don’t quite understant why people are so afraid of app servers and are so willing do to the same things with spring.

    Regards

  • http://blog.newsplore.com/?fbconnect_action=myhome&userid=2 Florin

    You forgot about my remarks re query caching. Putting cache management into the code is backwards and a bad practice.
    JPA spec is JSR driven (220 for 1.0 and 317 for 3.0) so it’s not about sharing a standard, it’s about being compliant and EclipseLink will be the JPA 2.0 RI.
    Regarding using Tomcat&Spring vs. JEE container there’s a ton of documentation on the internet that will tell you why the former combo is a winner.

    florin

  • http://tecrepublic.blogspot.com mcahornsirup

    Nice article. I worked with toplink and it was always a pleasure – especially together with the Netbeans IDE. Now, toplink is eclipselink and I tried to use it for an ORM to Postgres/PosGIS. It took me a whole 2 days to find the approoriate solution – a Session Customizer! I also googled for an hibernate solution and stumbled over a solution multiple times… somehow, I would love to use eclipselink, but it is really painful to use it. I really hope, that it will improve as it gains popularity through the eclipse project.

  • blorg

    I just had to migrate from Hibernate to EclipseLink due to a non-technical decision from above. The whole exercise was nothing but pain and now we have less functionality than before. The fun:
    – getting it to work with HSQLDB (used for all our unit tests) was pain
    – PersistenceUnitProperties.DROP_AND_CREATE does not work with postgresql so automatically re-creating the schema as required during development (Hibernate’s “hibernate.hbm2ddl.auto”, “create-drop” works of course)
    – documentation blows; the site and third party (look for EclipseLink/TopLink on Amazon and then try Hibernate)
    – lots of skilled Hibernate coders available; not so much for EclipseLink
    – online docs are peppered with Oracle specific code that can sneak in thus making your ORM choice locked to Oracle DB

    I am not an ORM expert but any time I have used Hibernate it just worked. For basic use I see zero benefit to EclipseLink so far. Maybe it is targeted for pro users with needs not covered in this discussion?

    I suspect the only people using EclipseLInk/TopLink are those using Oracle’s frameworks/dev tools and Oracle DBs and it may be just great for that. But if you want to use other DBs and do things manually good luck.

  • Samba Siva Rao

    I have used Eclipselink to persist data to PostgreSQL, MySQL, and Oracle; and have never faced an issue that made me rethink my choice for persistence.

    Just because you are conversant with Hibernate, you felt using it easier, for some one who have used Toplink/eclipselink only would have felt the same with respect to Hibernate.

    There may be difference in the way a feature or two are implemented in either of the frameworks, since these are proprietary features and there has been no standard so far. Even Hibernate moved to the annotations way of doing things only after realizing that JPA is taking advantage of annotations; eclipselink may be slow on that bandwagon, but it will catch up. In fact, eclipselink 1.2 is a lot more easier to use than Hibernate is.

    When it comes to using Session Customizer on Tomcat, it is very obvious that Oracle users would not use Tomcat for deployment, and hence Toplink did not have a need to address this particular use case; but Eclipselink being open sourced, it would be better to simplify the deployment on tomcat.