June 21, 2011

MOXy Extensible Models - Multiple Versions

Recently I have discussed support for extensible models in EclipseLink JAXB (MOXy) 2.3. The metadata for the extensions is supplied via MOXy's mapping document.  In this post I will demonstrate how to leverage MOXy's support for multiple mapping documents to keep your metadata organized.


XML Metadata

I have demonstrated MOXy's mapping documents in previous posts.  In EclipseLink 2.3 we've added the ability this metadata via multiple mapping documents.  This could be leveraged in the following ways:
  • Maintain separate mapping documents for static and virtual properties
  • Each version of your application could isolate its metadata changes in a separate mapping document.

Real Properties (static-binding.xml)

This set of mappings corresponds to real properties in our domain model.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="blog.metadatasource.multiple">
    <xml-schema 
        namespace="http://www.example.com/customer" 
        element-form-default="QUALIFIED"/>
    <java-types>
        <java-type name="Customer">
            <xml-virtual-access-methods/>
            <xml-root-element/>
            <xml-type prop-order="firstName lastName address"/>
            <java-attributes>
                <xml-element 
                    java-attribute="firstName" 
                    name="first-name"/>
                <xml-element 
                    java-attribute="lastName" 
                    name="last-name"/>
                <xml-element 
                    java-attribute="billingAddress" 
                    name="billing-address"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Virtual Properties (virtual-binding-v1.xml)

This set of mappings corresponds to the virtual properties that were added to our application in the first version.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="blog.metadatasource.multiple">
    <java-types>
        <java-type name="Customer">
            <java-attributes>
                <xml-element 
                    java-attribute="middleName" 
                    name="middle-name" 
                    type="java.lang.String"/>
                <xml-element 
                    java-attribute="shippingAddress" 
                    name="shipping-address" 
                    type="blog.metadatasource.multiple.Address"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Virtual Properties (virtual-binding-v2.xml)

This mapping document corresponds to the virtual properties that were added in the second version of the application.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="blog.metadatasource.multiple">
    <java-types>
        <java-type name="Customer">
            <java-attributes>
                <xml-attribute 
                    java-attribute="id" 
                    type="java.lang.Integer"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Demo

The mapping documents are passed in when creating the JAXBContext via the properties Map.  This properties accepts the mapping documents in many different formats (String, URL, Node, InputStream, XMLStreamReader, etc), and now in EclipseLink 2.3 you can pass in a list of mapping documents.

package blog.metadatasource.multiple;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(1);

        List<String> bindings = new ArrayList<String>(3);
        bindings.add("blog/metadatasource/multiple/static-binding.xml");
        bindings.add("blog/metadatasource/multiple/virtual-binding-v1.xml");
        bindings.add("blog/metadatasource/multiple/virtual-binding-v2.xml");
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindings);

        JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);

        Customer customer = new Customer();
        customer.set("id", 100);
        customer.setFirstName("Jane");
        customer.set("middleName", "Anne");
        customer.setLastName("Doe");

        Address billingAddress = new Address();
        billingAddress.setStreet("123 A Street");
        customer.setBillingAddress(billingAddress);

        Address shippingAddress = new Address();
        shippingAddress.setStreet("456 B Road");
        customer.set("shippingAddress", shippingAddress);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

Output

Below is the output from the demo code:

<?xml version="1.0" encoding="UTF-8"?>
<customer xmlns="http://www.example.com/customer" id="100">
   <first-name>Jane</first-name>
   <last-name>Doe</last-name>
   <billing-address>
      <street>123 A Street</street>
   </billing-address>
   <middle-name>Anne</middle-name>
   <shipping-address>
      <street>456 B Road</street>
   </shipping-address>
</customer>

Java Model

The following domain model is used in this example.

Customer

The @XmlVirtualAccessMethods annotation is used to specify that a class is extensible.  An extensible class is required to have a "get" method that returns a value by property name, and a "set" method that stores a value by property name.  The default names for these methods are "get" and "set", and can be overridden with the @XmlVirtualAccessMethods annotation.

package blog.metadatasource.refresh;

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;

@XmlRootElement
@XmlType(propOrder={"firstName", "lastName", "address"})
@XmlVirtualAccessMethods
public class Customer {

    private String firstName;
    private String lastName;
    private Address billingAddress;
    private Map<String, Object> extensions = new HashMap<String, Object>();

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Address getBillingAddress() {
        return billingAddress;
    }

    public void setBillingAddress(Address billingAddress) {
        this.billingAddress = billingAddress;
    }

    public Object get(String key) {
        return extensions.get(key);
    }

    public void set(String key, Object value) {
        extensions.put(key, value);
    }

}

Address

package blog.metadatasource.refresh;

public class Address {

    private String street;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

}

Further Reading

If you enjoyed this post you may also be interested in:
Other articles related to new MOXy features in EclipseLink 2.3:

2 comments:

  1. Hi Blaise,

    I am developing a POC for mutitenat application that enables flexi fields.The technologies used are Eclipselink,spirng and apache cxf.To enable the multi-tenat feature i am setting the tenant context property on entity manager(injected using @PersistentContext), as of now no issues.If am enabling flexing fields then per-tenant the entity meta-data would be different.So i should go for pertenant EMF.I am using @Transactional annotations for managing transactions.How can i use spring configurations to get per tenant EMF with a transaction manager attached to it......

    ReplyDelete
  2. Hi Prasobh,

    Would you mind asking this question on the EclipseLink forum or EclipseLink users mailing list (eclipselink-users@eclipse.org)? That way we can get the EclipseLink JPA folks in on the discussion too.

    -Blaise

    ReplyDelete