Sunday, April 17, 2011

Reconciling JPA with JAX-B

I've recently begun work on refining the manufacturing plant model that I had created a year or so ago.  Part of the work was to split up the object model into sub categories, and prevent cyclic dependencies between them.  I came up with three categories so far: static entities that are descriptions of physical real-world entities, such as parts, and shops, and vendors; inventory tracking records that keep track of all parts currently in the system, and historical records of parts no longer in the system; and various business documents that keep track of everything.


One of the problems I faced as I split out these categories, is all the bi-directional relationships defined.  When I first started to define the model, I basically made all relationships bi-directional figuring I would pare it down in the future as needed.  But all those bi-directional relationships create cyclic dependencies throughout the system, which impedes breaking things into modules that cannot have cyclic external dependencies, and problems if I was to use JAX-B to export the objects as XML.


For the most part changing bi-directional relationships in JPA is straight-forward, simply remove the list from the 'n' side of the relationship, and provide a query to replace it.  So for example in my object model, Stockroom used to contain a list of RealPartSet objects called inventory.  That has several problems, firstly anytime you need to get a Stockroom object, you drag it's list of inventory along with it.  That's not a big deal in small systems, but it certainly doesn't scale well.  The second problem, is that Stockroom is in the static entity module, and RealPartSet is in the part tracking module, so there cannot be a bi-directional relationship.  So I removed the list of RealPartSet objects from the Stockroom, created a new object called Inventory which contains a list of RealPartSet objects, and added a query to the Inventory object, findByStockroom.  Then anyplace I used to call Stockroom.getInventory(), now I call Inventory.findByStockroom(stockroom) to get the list.


One part that was a little more tricky, was in bi-directional many to many relationships.  For instance, a part may be source from several vendors, and each vendor supplies many different parts.  That is a real-world relationship that I could not dodge.  I could set up a cross-reference table, but my main goal in this project is to figure out how to best use JPA when modeling real-world problems.  So I continue to use the ManyToMany annotation, but I removed one side, in this instance, the vendor no longer has a list of the parts it supplies (which would have introduced a scaling problem anyway).  Now the problem of forming a query to fetch the list of parts supplied by a single vendor.  I looked it up in my books, and tried googling it, but found no results that specified exactly the JQL query that I would need.  Doing a more refined search brought me to  the code ranch which after reading down a bit suggested a query similar to this:


select p from Part p join p.vendors v where v.vendor = :vendor 


However that did not work it complained about v.vendor.  So I just tried a very simple approach with this query:

select p from Part p join p.vendors v where v = :vendor

That did the trick.  Now that I figured out the pattern, I applied it to many of the many-to-many relationships in the object model.

One final issue, using JAX-B bindings with JPA objects.  I would like to be able to use the JPA objects directly in webservices that use JAX-B to convert the Java objects to XML.  The problem is that JAX-B does not handle cyclic relationships well.  They have a number of solutions to creating XML from cyclic java objects, the first being to make the parent reference in the child object transient and filling it in later.  That would work well, if there is a single defined parent child relationship AND the child object never needs to be transmitted by itself.  The second approach uses XML id attributes to reference objects, but the problem is the id has to be unique in the whole document, and anything I would use as an ID is only unique for the class of objects.  There is a third way, some kind of interface that you can supply an implementation to resolve cycles 'on the fly', but the link provided in the JAX-B site is broke.  So I'll have to do some more research on that.

So I don't have an answer on how exactly I'm going to provide JAX-B bindings consistently on all my JPA objects.  I will have to research further and see if I can't figure out a good way to handle that without having to resort to the data-transfer-object anti-pattern.




No comments:

Post a Comment