Sunday, 27 July 2014

From Hibernate to EclipseLink

I've decided to try porting my little application from Hibernate to EclipseLink.

I am not currently using a lot of Hibernate specific functionality. The ones that occur to me at the moment is:
  • Hibernate Filters
  • Hibernate DELETE_ORPHAN

Filters

Filters are, as far as I know, currently not a part of the JPA Specification. Every ORM has its own implementation of dealing with it.

Below are the two different (very different!) implementations for Hibernate and EclipseLink. I find the Hibernate one to be more powerful.

Hibernate

Definition of the filter:
@FilterDef(name = "activePersons", defaultCondition="active = 1")
package mmud.database.entities;
Using the Filter on Entities:
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.Filters;

@Entity
@Filters({ @Filter(name = "activePersons") })
public class Person implements Serializable { ...
@Entity
public class Room implements Serializable {

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "room")
   @Filter(name = "activePersons")
   private Set<Person> persons = new HashSet<>();
Enabling the Filter, upon starting a session:
// Hibernate specific
Session session = 
    ((org.hibernate.ejb.EntityManagerImpl)em.getDelegate()).getSession();// JPA1.0
// Session session = getEntityManager().unwrap(Session.class); // JPA2.0
session.enableFilter("activePersons");

EclipseLink

EclipseLink uses the AdditionalCriteria2 3 annotation on Entity level.
@Entity
@AdditionalCriteria("(:activePersonFilter <> 1 or this.active = 1)")
public class Person implements Serializable { ...
...
...
getEntityManager().setProperty("activePersonFilter", 1); // turns filter on

Unfortunately, I cannot not set it, as that will trigger the following:
org.eclipse.persistence.exceptions.QueryException.missingContextPropertyForPropertyParameterExpression(QueryException.java:260)
So I am obligated to turn the filter on or off at the boundary where the entityManager (or EntityManagerFactory) is first called.

And there is not a convenient way of turning a filter on or off. Right now, the turning on/off is part of the subquery.4

However, I do like the fact that my AdditionalCriteria suddenly works everywhere once I turn it on. I do not have to set specifics on the fields of an Entity. Of course, this does limit the flexibility, but in my case it is not an issue.

Deleting Orphans

Well, Hibernate was one of the first to implement the Delete-orphan functionality and it took a while for it to become meanstream in the JPA specification. But it's there now, and should be supported by all ORMs.

Hibernate

@OneToMany(cascade = CascadeType.ALL, mappedBy = "belongsto")
@Cascade({ org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
private Set<item> items;

JPA 2.0

@OneToMany(cascade = CascadeType.ALL, mappedBy = "belongsto", orphanRemoval = true)
private Set<item> items;

Sha1

Hibernate

Apparently JPA doesn't have an sha1 function, yet mysql does. Hibernate had no problems with it, so I looked for a solution from EclipseLink5.
@NamedQuery(name = "User.authorise", query = "select p from Person p WHERE p.name = :name and p.password = sha1(:password)")

JPA 2.1

It turns out JPA5 has a specific reserved word called "FUNCTION" to support this, that I quite like. It prevents me from having to write a specific MySQL Dialect.

Of course, it does tie me to the MySQL implementation, but you cannot have everything.
@NamedQuery(name = "User.authorise", query = "select p from Person p WHERE p.name = :name and p.password = FUNCTION('sha1', :password)")

Notes

I found EclipseLink to be very strict with the interpretation of JPQL.

Issue 1 - References

For example, it complains about:
SELECT max(id) FROM ItemDefinition i
Wanting something more along the lines of:
SELECT max(i.id) FROM ItemDefinition i

Issue 2 - Like statements

Then there's an issue with the following query statement:
SELECT s FROM SillyName s WHERE :name like s.name
That gave me the error message "You have attempted to set a value of type class java.lang.String for parameter name with expected type of class java.lang.Boolean". I had to do some voodoo magic to make it work:
SELECT s FROM SillyName s WHERE concat('',:name) like s.name
Yikes!

Issue 3 - Update statements

Furthermore, I got a warning when creating the following update statement:
UPDATE Item i SET i.container = :container WHERE i = :item and i.belongsto = :person
The warning was:
“Input parameters can only be used in the WHERE clause or HAVING clause of a query.”
Apparently JPQL doesn't allow named parameters (or parameters of any kind) outside a WHERE or HAVING clause.

This was only a warning and the thing does work, but I should heed it.

It is described in Chapter 10.2.5.4. JPQL Input Parameters of the JPQL Language Spec7

Netbeans

I especially liked the fact that in Netbeans, I can mouse over the NamedQueries in the Entities and it tells me in a popup what is wrong.

References

[1] The Java EE 7 Tutorial - Introduction to the Java Persistence API
http://docs.oracle.com/javaee/7/tutorial/doc/persistence-intro001.htm
[2] AdditionalCriteria
http://wiki.eclipse.org/EclipseLink/Development/AdditionalCriteria
[3] AdditionCriteria
https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Mapping/Additional_Criteria
[4] StackOverflow - Is there a way to disable additionalcriteria in EclipseLink?
http://stackoverflow.com/questions/15847792/is-there-a-way-to-disable-additionalcriteria-in-eclipselink
[5] StackOverflow - JPA How to persist column with sha1 encryption
http://stackoverflow.com/questions/7868939/jpa-how-to-persist-column-with-sha1-encryption
[6] EclipseLink UserGuide - Support for Native Database Functions
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/Support_for_Native_Database_Functions#FUNC
[7] Oracle - JPQL Language References
http://docs.oracle.com/cd/E17904_01/apirefs.1111/e13946/ejb3_langref.html

No comments:

Post a Comment