Thursday, August 5, 2010

Using partial EclipseLink JPA entities with JAXB

I spent much of the final months of the EclipseLink 2.1 (Helios) development championing the enhancements of our existing FetchGroup support to address additional uses cases around working with partial entities. The new functionality is referred to as AttributeGroup with support for FETCH (what columns get retrieved from the database), LOAD (what relationships are populated), COPY (what attributes and relationships and populated in detached copy), and MERGE (what attributes are merged from a detached partial entity). This additional support greatly simplifies application development where detached entities are used as well as providing additional performance tuning options and flexibility for other scenarios.

The purpose of this blog post is to help a user we were assisting on Twitter who wanted to use JAXB to marshal JPA entities into XML but only wanted a portion of the entities to be used. By default JAXB will traverse all available relationships including lazy ones that have not been loaded to build a complete XML document for the provided entities.

Here is a simple example reading a Customer with an Address (1:1) and PhoneNumber(s) (1:M) from a database and marshalling it into XML using EclipseLink JPA with EclipseLink MOXy.

Setup code for JPA and JAXB:
// JPA
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("CustomerService");
EntityManager em = emf.createEntityManager();

// JAXB
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
true);
Example: Find & Marshall Customer
Customer customer = em.find(Customer.class, 1);
marshaller.marshal(customer, System.out);
When executed this generates the following XML and causes the customer's address and phone numbers to be loaded from the database.
Now, if we wanted to generate a partial XML document including only the Customer's basic attributes and its address's attributes we could use a copy group to create a partial copy and marshal it:
Customer customer = em.find(Customer.class, (long) 1);

CopyGroup cg = new CopyGroup();
cg.addAttribute("id");
cg.addAttribute("firstName");
cg.addAttribute("lastName");
cg.addAttribute("address.id");
cg.addAttribute("address.city");
cg.addAttribute("address.street");

JpaEntityManger nativeEM = em.unwrap(JpaEntityManager.class);
Customer custCopy = (Customer) nativeEM .copy(customer, cg);

marshaller.marshal(custCopy , System.out);
Now the generated XML appears as:

There are other combined uses of AttributeGroup that can be used to control what is fetched and loaded as well but this copy example should illustrate some of the options available and how EclipseLink JPA and MOXy components can be easily used together.

Doug

Additional MOXy examples available here.