Wednesday, December 12, 2012

FIQL and JPA2 Queries In Action

I've been focusing quite a lot recently on enhancing CXF Search extension module, by improving the existing converters and creating the new ones, making sure the parser is configurable, flexible and capable of mapping arbitrary property names to the properties of the bean capturing the search expression, and improving the documentation.

Andy Michalec created a FIQL parser quite a long time ago, it's been around for a while really, but it is only since Jeff Wang provided an initial FIQL to JPA2 converter patch that it kind of struck me how important it was to ensure the utility converters for mainstream and most popular technologies for querying the data stores were available. 

With all the documentation and code improvements, it is still not quite good till it is actually demonstrated somehow, and this is exactly what I've spent the last few days upon, on enhancing the existing Talend ESB jaxrs-advanced demo to show how the client can type the queries with SearchConditionBuilder without having to type FIQL expressions manually and get the expressions seamlessly converted to JPA2 TypedQuery and CriteriaQuery, with Tuple mixed in, and the matching data returned to the client, all with the help of FIQL to JPA2 converter.

Let me comment on the actual demo enhancements in more detail.

First check the client code, the "useSearchService" function, starting from the line 194 and more specifically from line 205. IMHO expressing the query with the help of the fluent query builder (also created by Andy) is quite cool, it is definitely more descriptive with respect to what is actually expected, and it is the specific query language agnostic, it is FIQL by default but can be something else.
The builder can build whatever advanced query expression is needed, though practically speaking I would not expect massive expressions being created.

Now lets move to the server code. SearchService is one which actually handles the client requests and all it does it delegates to PersonInfoStorage which deals with querying the data. Note, SearchService works with CXF SearchContext  by expecting it to extract the search expression from the current URI query component (default mode) or getting the expression from URI path and submitting it to SearchContext.

The main and in fact the single demo domain entity is Person but it is the one which is recursive, with children and parents, ancestors and descendants linked to. I haven't modified these relationships to get CXF JPA2 converter working properly, the only thing I did I added a couple of missing Person setters, added JPA2 annotations and also a hack required for JAXB RI capable of dealing with recursive structures (see the end of Person code).

The Person model is initialized in PersonInfoStorage init method (see the end of the file), where the injected JPA2 EntityManager is used - it could've been done elsewhere but was good enough for me for the purpose of the demo.

Next check getTypedQueryPerson (line 68) - see how straightforward it is  to get the FIQL expression transparently converted to JPA2 TypedQuery, the comments in the code should make it very easy to understand.

Note, Person is a recursive and possibly a very deep structure and one may not necessarily want to fetch all the Person representation back to the client. A number of options exists for dealing with this issue including using the intrusive JAXB XmlTransient annotation but using JPA2 Tuple is one of the most elegant ones.

Using Tuple is one of JPA2 options for having the response shaped into simpler or different structures and getCriteriaQueryPerson  method shows how it can be easily done by having the query returning the data sufficient for initializing a simple PersonInfo representation. One needs to get OpenJPA auto-generating the
metamodel classes for the tuple selections working and you can see how it can be done here, this tip helped me a lot. 

Also note that PersonInfoStorage has the injected bean properties passed to SearchContext. Why this may have to be done is explained here but in short it lets to get the property names used in query language completely decoupled from the corresponding properties of the capturing bean, for example, in the demo case, the client can type "builder.is('childName').equalTo('Fred')" and have it working with the 'childName'  correctly mapped to "children.name", where "children" points to a Person collection of children. This makes it simpler and easier to work with for the client, while keeping the internals of how the properties are actually linked to each other completely opaque. That is cool, I can hear you saying :-)

Finally have a look at how JPA2 EntityManager is initialized and injected. I started with arguably a simpler approach, I basically used Spring ORM module to get the entity manager wired in, see this beans.xml.  This actually back-fired on me when I tried to make the demo working in Karaf - Spring ORM needs to see persistence.xml and is not capable on its own of inspecting OSGI Meta-Persistence property set up in the common module's pom.xml so I just ended up duplicating persistence.xml in both common and service modules.

On the TODO list is to follow an excellent tutorial from Christian and make the demo working with OSGi JPA Service. Perhaps someone from the community will be interested in doing a related pull request ?
  
So this is it and hope you'll find something interesting in this demo, enjoy !  

No comments: