Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/MOXy/Advanced Concepts/Virtual Access Methods"

m (Replacing page with 'See http://www.eclipse.org/eclipselink/documentation/2.4/moxy/advanced_concepts003.htm')
 
(10 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{EclipseLink_UserGuide
+
See http://www.eclipse.org/eclipselink/documentation/2.4/moxy/advanced_concepts003.htm
|eclipselink=y
+
|eclipselinktype=MOXy
+
|info=y
+
|api=y
+
|apis= * [http://www.eclipse.org/eclipselink/api/latest/org/eclipse/persistence/oxm/annotations/XmlVirtualAccessMethods.html XmlVirtualAccessMethods]
+
* [http://www.eclipse.org/eclipselink/api/latest/javax/xml/bind/annotation/XmlAccessorType.html XmlAccessorType]
+
|toc=y
+
}}
+
 
+
= Virtual Access Methods =
+
 
+
In addition to standard JAXB properties (represented by Java fields and accessor methods), EclipseLink MOXy '''2.3''' introduces the concept of virtual properties and virtual access methods, which instead rely on special '''get()''' and '''set()''' methods to maintain mapping data.  For example, you might want to use a '''HashMap''' as the underlying structure to hold data for certain mappings.  The mappings that use virtual method access must be defined in EclipseLink OXM metadata.
+
 
+
In order to add virtual properties to an entity:
+
 
+
* the Java class must be marked with an '''@XmlVirtualAccessMethods''' annotation, or '''<xml-virtual-access-methods>''' element in OXM
+
* the Java class must contain getter and setter methods to access virtual property values
+
** '''public Object get(String propertyName)'''
+
** '''public void set(String propertyName, Object value)'''
+
** method names are configurable but must have the same method signatures as above
+
 
+
 
+
<div style="width:800px">
+
{{tip||By default, EclipseLink will look for methods named "set" and "get".  To customize accessor method names, see [[#Specifying Alternate Accessor Methods|Specifying Alternate Accessor Methods]].}}
+
</div>
+
 
+
 
+
== Configuration ==
+
 
+
Virtual Access Methods can be configured either through Java annotations or EclipseLink OXM metadata.
+
 
+
=== Using Annotations ===
+
 
+
<div style="width:700px">
+
<source lang="java">
+
package example;
+
 
+
import java.util.Map;
+
import java.util.HashMap;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
+
 
+
@XmlRootElement
+
@XmlVirtualAccessMethods
+
@XmlAccessorType(AccessType.PROPERTY)
+
public class Customer {
+
+
  private int id;
+
+
  private String name;
+
+
  private Map<String, Object> extensions = new HashMap<String, Object>();
+
+
  public Object get(String name) {
+
      return extensions.get(name);
+
  }
+
+
  public void set(String name, Object value) {
+
      extensions.put(name, value);
+
  }
+
+
  @XmlAttribute
+
  public int getId() {
+
  ...
+
+
}
+
</source>
+
</div>
+
 
+
=== Using EclipseLink OXM ===
+
 
+
<div style="width:700px">
+
<source lang="xml">
+
...
+
<java-types>
+
  <java-type name="Customer">
+
      <xml-virtual-access-methods />
+
      <java-attributes>
+
        <xml-attribute java-attribute="id" />
+
        <xml-element java-attribute="name" />
+
      </java-attributes>
+
  </java-type>
+
...
+
</source>
+
</div>
+
 
+
 
+
== Example ==
+
 
+
For this example we will use the '''Employee''' class above, along with an EclipseLink OXM file to define our virtual mappings.  Any property encountered in this file that does not have a corresponding Java attribute will be considered a virtual property and will be accessed through the virtual access methods.  Because there is no associated Java field, the '''type''' information must also be provided.
+
 
+
'''virtualprops-oxm.xml'''
+
<div style="width:700px">
+
<source lang="xml">
+
...
+
<java-types>
+
    <java-type name="Customer">
+
        <java-attributes>
+
            <xml-element java-attribute="discountCode" name="discount-code"
+
                type="java.lang.String" />
+
        </java-attributes>
+
    </java-type>
+
</java-types>
+
...
+
</source>
+
</div>
+
 
+
When creating the '''JAXBContext''', we pass in the '''virtualprops''' metadata along with our '''Customer''' class.
+
 
+
To set the values for virtual properties, we will use the aforementioned '''set()''' method.
+
 
+
<div style="width:700px">
+
<source lang="java">
+
InputStream oxm = classLoader.getResourceAsStream("virtualprops-oxm.xml");
+
Map<String, Object> properties = new HashMap<String, Object>();
+
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, oxm);
+
 
+
Class[] classes = new Class[] { Customer.class };
+
JAXBContext ctx = JAXBContext.newInstance(classes, properties);
+
 
+
Customer c = new Customer();
+
c.setId(7761);
+
c.setName("Bob Smith");
+
c.set("discountCode", "SIUB372JS7G2IUDS7");
+
 
+
ctx.createMarshaller().marshal(e, System.out);
+
</source>
+
</div>
+
 
+
This will produce the following XML:
+
 
+
<div style="width:700px">
+
<source lang="xml">
+
<customer id="7761">
+
  <name>Bob Smith</name>
+
  <discount-code>SIUB372JS7G2IUDS7</discount-code>
+
</customer>
+
</source>
+
</div>
+
 
+
Conversely, we use the '''get(String)''' method to access virtual properties:
+
 
+
<div style="width:700px">
+
<source lang="java">
+
...
+
Customer c = (Customer) ctx.createUnmarshaller().unmarshal(CUSTOMER_URL);
+
 
+
// Populate UI
+
customerWindow.getTextField(ID).setText(String.valueOf(c.getId()));
+
customerWindow.getTextField(NAME).setText(c.getName());
+
customerWindow.getTextField(DCODE).setText(c.get("discountCode"));
+
...
+
</source>
+
</div>
+
 
+
 
+
== XmlAccessorType and XmlTransient ==
+
 
+
If you are using an '''@XmlAccessorType''' other than '''AccessType.PROPERTY''', you will need to mark your virtual properties Map attribute to be '''@XmlTransient''', to prevent the Map itself from being bound to XML.
+
 
+
<div style="width:700px">
+
<source lang="java">
+
package example;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
+
 
+
@XmlRootElement
+
@XmlVirtualAccessMethods
+
@XmlAccessorType(AccessType.FIELD)
+
public class Customer {
+
 
+
  @XmlTransient
+
  private Map<String, Object> extensions;
+
  ...
+
</source>
+
</div>
+
 
+
 
+
== Options ==
+
 
+
 
+
=== Specifying Alternate Accessor Methods ===
+
 
+
To use different method names as your virtual method accessors, specify them using the '''getMethodName''' and '''setMethodName''' attributes on '''@XmlVirtualAccessMethods''':
+
 
+
<div style="width:700px">
+
<source lang="java">
+
package example;
+
 
+
import java.util.Properties;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
+
 
+
@XmlRootElement
+
@XmlVirtualAccessMethods(getMethod = "getCustomProps", setMethod = "putCustomProps")
+
@XmlAccessorType(AccessType.FIELD)
+
public class Customer {
+
 
+
  @XmlAttribute
+
  private int id;
+
 
+
  private String name;
+
 
+
  @XmlTransient
+
  private Properties<String, Object> props = new Properties<String, Object>();
+
 
+
  public Object getCustomProps(String name) {
+
      return props.getProperty(name);
+
  }
+
+
  public void putCustomProps(String name, Object value) {
+
      props.setProperty(name, value);
+
  }
+
 
+
}
+
</source>
+
</div>
+
 
+
In OXM:
+
 
+
<div style="width:800px">
+
<source lang="xml">
+
...
+
<java-types>
+
  <java-type name="Customer">
+
    <xml-virtual-access-methods get-method="getCustomProps" set-method="putCustomProps" />
+
    <java-attributes>
+
      <xml-attribute java-attribute="id" />
+
      <xml-element java-attribute="name" />
+
      <!-- virtual -->
+
      <xml-element java-attribute="discountCode" name="discount-code"
+
        type="java.lang.String" />
+
    </java-attributes>
+
  </java-type>
+
...
+
</source>
+
</div>
+
 
+
 
+
=== Schema Generation Options ===
+
 
+
You can configure how virtual properties should appear in generated schemas using the '''schema''' attribute on '''@XmlVirtualAccessMethods'''.  EclipseLink offers two options: virtual properties can be written as individual nodes, or consolidated into a single '''<any>''' element.
+
 
+
 
+
'''Virtual Properties as individual Nodes'''
+
 
+
This is EclipseLink's default behaviour, or can be specified explicitly as an override as follows:
+
 
+
<div style="width:700px">
+
<source lang="java">
+
package example;
+
 
+
@XmlRootElement
+
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.NODES)
+
@XmlAccessorType(AccessType.FIELD)
+
public class Customer {
+
 
+
  ...
+
</source>
+
</div>
+
 
+
For example:
+
 
+
Original '''Customer''' Schema:
+
 
+
<div style="width:700px">
+
<source lang="xml">
+
<xs:schema ...>
+
 
+
    <xs:element name="customer">
+
        <xs:complexType>
+
            <xs:sequence>
+
                <xs:element name="first-name" type="xs:string" />
+
                <xs:element name="last-name" type="xs:string" />
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
 
+
</xs:schema>
+
</source>
+
</div>
+
 
+
Generated Schema after adding '''middle-initial''' and '''phone-number''':
+
 
+
<div style="width:700px">
+
<source lang="xml">
+
<xs:schema ...>
+
 
+
    <xs:element name="customer">
+
        <xs:complexType>
+
            <xs:sequence>
+
                <xs:element name="first-name" type="xs:string" />
+
                <xs:element name="last-name" type="xs:string" />
+
                <xs:element name="middle-initial" type="xs:string" />
+
                <xs:element name="phone-number" type="xs:string" />
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
 
+
</xs:schema>
+
</source>
+
</div>
+
 
+
 
+
'''Virtual Properties in an <any> Element'''
+
 
+
EclipseLink can also use an '''<any>''' element to hold all of the virtual properties in one node:
+
 
+
<div style="width:700px">
+
<source lang="java">
+
package example;
+
 
+
@XmlRootElement
+
@XmlVirtualAccessMethods(schema = XmlVirtualAccessMethodsSchema.ANY)
+
@XmlAccessorType(AccessType.FIELD)
+
public class Customer {
+
 
+
  ...
+
</source>
+
</div>
+
 
+
Taking the example from above, a newly generated schema using this approach would look like:
+
 
+
<div style="width:700px">
+
<source lang="xml">
+
<xs:schema ...>
+
 
+
    <xs:element name="customer">
+
        <xs:complexType>
+
            <xs:sequence>
+
                <xs:element name="first-name" type="xs:string" />
+
                <xs:element name="last-name" type="xs:string" />
+
                <xs:any minOccurs="0" />
+
            </xs:sequence>
+
        </xs:complexType>
+
    </xs:element>
+
 
+
</xs:schema>
+
</source>
+
</div>
+
 
+
== More Examples ==
+
 
+
For an example of using virtual properties in a multi-tenant architecture, see:
+
 
+
* [[EclipseLink/Examples/MOXy/Extensible|Extensible MOXy]]
+

Latest revision as of 09:30, 8 November 2012

See http://www.eclipse.org/eclipselink/documentation/2.4/moxy/advanced_concepts003.htm