Differences between Hibernate JPA and Toplink JPA

On one of our projects we decided to switch JPA providers from Hibernate to Toplink. The main reason for this was that Hibernate depends on so many core open source libraries that other open source projects also use, that there are all kinds of version problems when the application is deployed in a JEE container, such as Tomcat or Glassfish, that uses different versions internally. Basically jar hell with all kinds of issues with classloaders loading the wrong version of the jar.

In order to overcome this problem we decided to move to Toplink, besides it would be a good test of just how easy it was to switch providers. The short story is that there are some really annoying differences between the providers that make switching a bad idea, unless you programmed to the lowest common denominator originally. This would be quiet difficult though as I can't find a comprehensive list of the differences between the two anywhere. Here are some of the oddities I found.

Table-per-class Inheritance

If you have an entity subclass an existing entity and use table per class strategy Hibernate does not require a discriminator field in the super class/table. Hibernate will link the subclass to the parent class for queries on the subclass by matching the primary keys of the two tables. Toplink on the other hand requires a discriminator columns. This is , apparently, according to the specification and Hibernate is non-compliant in this regard. This means you will have to update all affected tables, ie their strucutre and data, as appropriate for your app to work when switching. I also think this makes it impossible to go back to Hibernate after the change but I did not verify this.

Toplink more pedantic, less flexible queries

What I found when switching to Toplink was that a lot of the Hibernate queries would not run anymore. In many respects Toplink is inferior to Hibernate's query implementation.

Hibernate has a better engine for aggregates and the + operator 

The following query works in Hibernate but not Toplink

"Select sum(item.amount+item.tax) from InvoiceItem where Invoice =?1"

The only way to get the above to work in toplink was to rewrite the query as a NativeQuery -- basically as SQL.

Toplink does not forgive sloppy use of aliases

Hibernate is also more forgiving if you forget to put the entity alias in from of your field. This work in Hibernate

"Select t from  Traffic t where bytes > 1000"

In Toplink you cant leave off the alias it must be

"Select t from Traffic t where t.bytes > 1000"

This was a problem in a "order by" clause as well, but was not identified as the source of the problem by the trace stack so if you get some seamingly inexplixable error check your use of aliases.

Hibernate better at the "in" operator

Hibernate also correctly compiled the following subquery used in a "in operator" into SQL.

"Select u from  Usage u where u.customer not in (Select s.customer from Suspension s)"

Under Toplink the query that got compiled would not work because it returned all the object fileds and the code had to be rewritten to 

 "Select u from Usage u where u.customer.id not in (Select s.customer.id from Suspension s)"

 Hibernate allows use of Strings for Enum comparison

In the cases where we used enums for fields the query could be written as follows:

"Select r from ReadyQue r where r.status='READY'"

Here status is an enum field. In Toplink this had to be written as:

"Select r from ReadyQue r where r.status=za.co.jumpingbean.model.Status.READY"

Caching Issues

In hibernate I had to call refresh on a entity after persisting it to get the proper Id value. In toplink calling refresh without calling flush first caused a "Entity has been removed" error. In toplink we just commented out the refresh call. I actually can't remember why we needed the refresh call in Hibernate. Maybe it was just bad code.


Lets hope that JPA moved closer to a unified query language with the next version. Also lets hope the allow the use of the in operator on collections such as 

"Select r from ReadyQue where r.customer in ?1",customers"