Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Development/DynamicOXM
Dynamic Persistence with EclipseLink OXM
This page provides an overview for the work done to support Dynamic Persistence in EclipseLink OXM.
Document History
Date | Author | Version Description & Notes |
---|---|---|
091117 | Rick Barkhouse | 1.0 |
Contents
Overview
The first step in creating a JPA-JAXB meet-in-the-middle solution is to enable EclipseLink OXM to use Dynamic Persistence, a.k.a. processing Projects, Mappings & Descriptors without having concrete Java classes available. This work builds off of the initial Dynamic Persistence functionality (org.eclipse.persistence.dynamic
) current used by the JPA component. By supplying a DynamicClassLoader
and loading the EclipseLink project by way of DynamicTypeBuilder.loadDynamicProject()
, we can inject Dynamic Persistence functionality into EclipseLink OXM in a fairly transparent way.
Prototype
We currently have a prototype available that can read an EclipseLink project, unmarshal an XML document, modify the Java domain objects (using reflection), and marshal the modified objects, all without having actual Java class files available on the classpath. To get this prototype working the following changes were required:
DynamicTypeBuilder (org.eclipse.persistence.dynamic)
- A new method to load a dynamic Project by passing in a "dry" EclipseLink project (one with only class names defined) was added for convenience:
public static Project loadDynamicProject(Project project, DatabaseLogin login, DynamicClassLoader dynamicClassLoader)
- A change was needed to avoid a ClassCastException (because we are now potentially dealing with XML mappings):
private boolean requiresInitialization(DatabaseMapping mapping) { ... if (mapping.isAggregateMapping() && !mapping.isXMLMapping()) { return !((AggregateObjectMapping) mapping).isNullAllowed(); } ... }
OXM Back-Pointer Support
Some changes were needed in the way that back-pointers are configured in OXM. Previously, a back-pointer would be configured on the source descriptor's mapping, e.g.:
... XMLCompositeCollectionMapping phone = new XMLCompositeCollectionMapping(); phone.setAttributeName("phoneNumbers"); phone.setXPath("phone-numbers/number"); phone.setReferenceClassName("rick.dynamic.XPhoneNumber"); ... phone.setContainerAttributeName("owningEmployee"); phone.setContainerGetMethodName("getOwningEmployee"); phone.setContainerSetMethodName("setOwningEmployee"); ...
The problem is that when Dynamic Types are being created, class attributes are created for each of the descriptor's mappings; and since the owningEmployee
attribute is not mapped on the Phone
descriptor, one would not be generated on the dynamic Phone
class.
The solution was to create a new type of mapping (tentatively XMLBackReferenceMapping
) that can be set on the target descriptor, and delegate it's functionality back to the original back-pointer code in XMLCompositeObjectMapping
:
public class XMLBackReferenceMapping extends AggregateMapping implements ContainerMapping { ... @Override public void postInitialize(AbstractSession session) throws DescriptorException { // Get the corresponding mapping from the reference descriptor and set up the // back-pointer policy. DatabaseMapping mapping = getReferenceDescriptor().getMappingForAttributeName(this.mappedBy); if (mapping instanceof XMLCompositeCollectionMapping) { XMLCompositeCollectionMapping oppositeMapping = (XMLCompositeCollectionMapping) mapping; oppositeMapping.setBackReferenceMapping(this); } } ...
With these changes, back-pointers are now configured as follows:
// EMPLOYEE Descriptor XMLCompositeCollectionMapping phone = new XMLCompositeCollectionMapping(); phone.setAttributeName("phoneNumbers"); phone.setXPath("phone-numbers/number"); phone.setReferenceClassName("org.eclipse.persistence.testing.oxm.dynamic.XPhoneNumber"); phone.useCollectionClass(ArrayList.class); phone.setContainerPolicy(ContainerPolicy.buildPolicyFor(ArrayList.class)); descriptor.addMapping(phone); // PHONE Descriptor XMLBackReferenceMapping owningEmployee = new XMLBackReferenceMapping(); owningEmployee.setReferenceClassName("org.eclipse.persistence.testing.oxm.dynamic.XEmployee"); owningEmployee.setMappedBy("phoneNumbers"); owningEmployee.setAttributeName("owningEmployee"); owningEmployee.setSetMethodName("setOwningEmployee"); owningEmployee.setGetMethodName("getOwningEmployee"); descriptor.addMapping(owningEmployee);
What was previously containerAttributeName/containerGetMethodName/containerSetMethodName
on XMLCompositeCollectionMapping
is now accessed through that mapping's new backReferenceMapping
field.
Several changes were required throughout OXM to support this change, e.g.:
if (xmlCompositeCollectionMapping.getBidirectionalPolicy().getBidirectionalTargetContainerPolicy() == null) { xmlCompositeCollectionMapping.getContainerAccessor().setAttributeValueInObject(objectValue, unmarshalRecord.getCurrentObject()); }
becomes
if (xmlCompositeCollectionMapping.getBackReferenceMapping().getContainerPolicy() == null) { xmlCompositeCollectionMapping.getContainerAccessor().setAttributeValueInObject(objectValue, unmarshalRecord.getCurrentObject()); }