Antonio Goncalves is a senior software architect living in Paris. Initially focused on Java development since the late 1990s, his career has taken him to different countries and companies where he works now as a Java EE consultant in software architecture. As a former BEA consultant he has a great expertise in application servers such as Weblogic, JBoss and, of course, GlassFish. He is particularly fond of Open Source and is a member of the OOSGTP (Open Source Get Together Paris). He is also the co-creator of the Paris Java User Group and talks on Les Cast Codeurs podcast. Antonio wrote a first book in French on Java EE 5 back in 2007. Since then he has join the JCP and is an Expert Member of various JSRs (Java EE 6, JPA 2.0 and EJB 3.1). He then published a second book for Apress: Beginning Java EE 6 Platform with GlassFish 3. For the last years Antonio has been talking at international conferences mainly about Java EE, including JavaOne, The Server Side Symposium, Devoxx, Jazoon… He has also written numerous technical papers and articles for IT Web sites (DevX, JaxEnter) or IT magazines (Programmez, Linux Magazine). Antonio is a DZone MVB and is not an employee of DZone and has posted 18 posts at DZone. You can read more from them at their website. View Full User Profile

How to Get the JPQL/SQL String From a CriteriaQuery in JPA ?

06.04.2012
| 4511 views |
  • submit to reddit

I.T. is full of complex things that should (and sometimes could) be simple. Getting the JQPL/SQL String representation for a JPA 2.0 CriteriaQuery is one of them.

By now you all know the JPA 2.0 Criteria API : a type safe way to write a JQPL query. This API is clever in the way that you don’t use Strings to build your query, but is quite verbose… and sometimes you get lost in dozens of lines of Java code, just to write a simple query. You get lost in your CriteriaQuery, you don’t know why your query doesn’t work, and you would love to debug it. But how do you debug it ? Well, one way would be by just displaying the JPQL and/or SQL representation. Simple, isn’t it ? Yes, but JPA 2.0 javax.persistence.Query doesn’t have an API to do this. You then need to rely on the implementation… meaning, the code is different if you use EclipseLink, Hibernate or OpenJPA.

The CriteriaQuery we want to debug

Let’s say you have a simple Book entity and you want to retrieve all the books sorted by their id. Something like SELECT b FROM Book b ORDER BY b.id DESC. How would you write this with the CriteriaQuery ? Well, something like these 5 lines of Java code :

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> q = cb.createQuery(Book.class);
Root<Book> b = q.from(Book.class);
q.select(b).orderBy(cb.desc(b.get("id")));
TypedQuery<Book> findAllBooks = em.createQuery(q);

So imagine when you have more complex ones. Sometimes, you just get lost, it gets buggy and you would appreciate to have the JPQL and/or SQL String representation to find out what’s happening. You could then even unit test it.

Getting the JPQL/SQL String Representations for a Criteria Query

So let’s use an API to get the JPQL/SQL String representations of a CriteriaQuery (to be more precise, the TypedQuery created from a CriteriaQuery). The bad news is that there is no standard JPA 2.0 API to do this. You need to use the implementation API hoping the implementation allows it (thank god that’s (nearly) the case for the 3 main JPA ORM frameworks). The good news is that the Query interface (and therefore TypedQuery) has an unwrap method. This method returns the provider’s query API implementation. Let’s see how you can use it with EclipseLink, Hibernate and OpenJPA.

EclipseLink

EclipseLink‘s Query representation is the org.eclipse.persistence.jpa.JpaQuery interface and the org.eclipse.persistence.internal.jpa.EJBQueryImpl implementation. This interface gives you the wrapped native query (org.eclipse.persistence.queries.DatabaseQuery) with two very handy methods : getJPQLString() and getSQLString(). Unfortunatelly the getJPQLString() method will not translate a CriteriaQuery into JPQL, it only works for queries originally written in JPQL (dynamic or named query). The getSQLString() method relies on the query being “prepared”, meaning you have to run the query once before getting the SQL String representation.

findAllBooks.unwrap(JpaQuery.class).getDatabaseQuery().getJPQLString(); // doesn't work for CriteriaQuery
findAllBooks.unwrap(JpaQuery.class).getDatabaseQuery().getSQLString();

Hibernate

Hibernate‘s Query representation is org.hibernate.Query. This interface has several implementations and the very useful method that returns the SQL query string : getQueryString(). I couldn’t find a method that returns the JPQL representation, if I’ve missed something, please let me know.

findAllBooks.unwrap(org.hibernate.Query.class).getQueryString()

OpenJPA

OpenJPA‘s Query representation is org.apache.openjpa.persistence.QueryImpl and also has a getQueryString() method that returns the SQL (not the JPQL). It delegates the call to the internal org.apache.openjpa.kernel.Query interface. I couldn’t find a method that returns the JPQL representation, if I’ve missed something, please let me know.

findAllBooks.unwrap(org.apache.openjpa.persistence.QueryImpl.class).getQueryString()

Unit testing

Once you get your SQL String, why not unit test it ? Hey, but I don’t want to test my ORM, why would I do that ? Well, it happens that I’ve discovered a but in the new releases of OpenJPA by unit testing a query… so, there is a use case for that. Anyway, this is how you could do it :

assertEquals("SELECT b FROM Book b ORDER BY b.id DESC", 
findAllBooksCriteriaQuery.unwrap(org.apache.openjpa.persistence.QueryImpl.class).getQueryString());

Conclusion

As you can see, it’s not that simple to get a String representation for a TypedQuery. Here is a digest of the three main ORMs :

ORM Framework Query implementation How to get the JPQL String How to get the SPQL String
EclipseLink JpaQuery getDatabaseQuery().getJPQLString()* getDatabaseQuery().getSQLString()**
Hibernate Query N/A getQueryString()
OpenJPA QueryImpl getQueryString() N/A

(*) Only possible on a dynamic or named query. Not possible on a CriteriaQuery
(**) You need to execute the query first, if not, the value is null

To illustrate all that I’ve written simple test cases using EclipseLink, Hibernate and OpenJPA that you can download from GitHub. Give it a try and let me know.

And what about having an API in JPA 2.1 ?

For a developers’ point of view it would be great to have two methods in the javax.persistence.Query (and therefore javax.persistence.TypedQuery) interface that would be able to easily return the JPQL and SQL String representations, e.g : Query.getJPQLString() and Query.getSQLString(). Hey, that would be the perfect time to have it in JPA 2.1 that will be shipped in less than a year. Now, as an implementer, this might be tricky to do, I would love to ear your point of view on this.

Anyway, I’m going to post an email to the JPA 2.1 Expert Group… just in case we can have this in the next version of JPA ;o)

References

 

 

 

 

 

 

 

 

Published at DZone with permission of Antonio Goncalves, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Fahmeed Nawaz replied on Tue, 2012/06/12 - 11:12am

If I want to connect to multiple databases in a session, I can just use the following each time? Does this cause any issues with the pooled connections or are they separate from the database associations?

The reason I want to do this is to only replicate one db but not the other.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.