| 
| Moxy JAXB  annotations for closure table relationship [message #540667] | Wed, 16 June 2010 18:37  |  | 
| Eclipse User  |  |  |  |  | I have an top level object called Organization. I'm using a closure table pattern to allow for parent-child relationships. I'm using EclipseLink JPA to manage these objects and everything works exceptionally well. But now I want to expose my entity beans via a WebService. So I've been following the Moxy tutorials and annotating my entities. 
 I've managed to get things to work out rather well but my issue is when I marshal my Organization object I only get the current object and the associated Id's. But the other Organization objects do not appear in the XML.
 
 XML:
 
 
<?xml version="1.0" encoding="UTF-8"?>
<organization>
   <id>6</id>
   <name>USA</name>
   <level>2</level>
   <alias>USA</alias>
   <version>2</version>
   <ancestors>6</ancestors>
   <ancestors>1</ancestors>
   <descendents>6</descendents>
   <descendents>7</descendents>
</organization>
 
 Java object:
 
 
public class Organization {
	@Id
	@Column(name = "OrganizationId", nullable = false)
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@XmlID
	private Integer id;
	@JoinTable(name = "OrganizationAncestry", joinColumns = { @JoinColumn(name = "Ancestor", referencedColumnName = "OrganizationId", nullable = false) }, inverseJoinColumns = { @JoinColumn(name = "Descendent", referencedColumnName = "OrganizationId", nullable = false) })
	@ManyToMany(fetch = FetchType.EAGER)
	@XmlIDREF
	private Collection<Organization> descendents;
	@ManyToMany(mappedBy = "descendents", fetch = FetchType.EAGER)
	@XmlIDREF
	private Collection<Organization> ancestors;
....
 I understand the reason for utilizing the @XmlIDREF annotations, but shouldn't each one of the referenced entities appear once within the XML? From the XML listed above, shouldn't Organization 1 and 7 be marshalled into the XML in addition to 6?
 
 Thanks for the help...
 
 [Updated on: Mon, 28 June 2010 11:54] by Moderator |  |  |  | 
| 
| Re: Moxy JAXB  annotations for closure table relationship [message #540927 is a reply to message #540667] | Thu, 17 June 2010 12:06   |  | 
| Eclipse User  |  |  |  |  | Hello, 
 JAXB expects that everything mapping with ID/IDREF is also mapped with a containment (@XMLElement).  Using EclipseLink JAXB, below is one way you could map this to get XML that looks like:
 
 
 <?xml version="1.0" encoding="UTF-8"?>
<organization>
   <id>1</id>
   <descendents>2</descendents>
   <organization>
      <id>2</id>
      <descendents>3</descendents>
      <ancestors>1</ancestors>
   </organization>
   <organization>
      <id>3</id>
      <ancestors>2</ancestors>
   </organization>
</organization>
 First we need a stand-in object for Organization that has the necessary containment property.  We will call this AdaptedOrganization:
 
 
 @XmlRootElement(name="organization")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(AdaptedOrganizationCustomizer.class)
public class AdaptedOrganization {
    private Organization organization;
    @XmlElement(name="organization")
    private List<Organization> orphans = new ArrayList<Organization>();
    ...
}
 We will need to customize the mappings for this class so we will leverage the EclipseLink @XmlCustomizer annotation to get at the underlying framework:
 
 
 import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
public class AdaptedOrganizationCustomizer implements DescriptorCustomizer {
    @Override
    public void customize(ClassDescriptor descriptor) throws Exception {
        XMLCompositeObjectMapping organizationMapping = (XMLCompositeObjectMapping) descriptor.getMappingForAttributeName("organization");
        organizationMapping.setXPath(".");
        XMLCompositeCollectionMapping orphansMapping = (XMLCompositeCollectionMapping) descriptor.getMappingForAttributeName("orphans");
        orphansMapping.setContainerPolicy(new GrowingListContainerPolicy());
    }
}
 EclipseLink has something called a ContainerPolicy to handle collection/map types.  In this use case we will be modifying the orphans list as we marshal so we need to create a custom ContainerPolicy to handle this.  It is associated with the mapping in the above customizer snippet:
 
 
 
import java.util.ArrayList;
import java.util.List;
import org.eclipse.persistence.descriptors.changetracking.CollectionChangeEvent;
import org.eclipse.persistence.internal.queries.ListContainerPolicy;
public class GrowingListContainerPolicy extends ListContainerPolicy {
    @Override
    public CollectionChangeEvent createChangeEvent(Object collectionOwner,
            String propertyName, Object collectionChanged,
            Object elementChanged, int changeType, Integer index) {
        return null;
    }
    @Override
    public Class getContainerClass() {
        return ArrayList.class;
    }
    @Override
    public boolean hasNext(Object iterator) {
        return ((GrowingListIterator) iterator).hasNext();
    }
    @Override
    public Object iteratorFor(Object container) {
        return new GrowingListIterator((List<Object>) container);
    }
    @Override
    protected Object next(Object iterator) {
        return ((GrowingListIterator) iterator).next();
    }
    private static class GrowingListIterator {
        private int index;
        private List<Object> list;
        public GrowingListIterator(List<Object> list) {
            this.index = 0;
            this.list = list;
        }
        public Object next() {
            return list.get(index++);
        }
        public boolean hasNext() {
            return list.size() > index; 
        }
    }
}
 We will discover the orphan list as we marshal, we will use a marshal listener to accomplish this:
 
 
 
import javax.xml.bind.Marshaller;
import orphans.Organization;
public class OrganizationMarshalListener extends Marshaller.Listener {
    private AdaptedOrganization adaptedOrganization;
    public OrganizationMarshalListener(Organization organization) {
        this.adaptedOrganization = new AdaptedOrganization();
        this.adaptedOrganization.setOrganization(organization);
    }
    public AdaptedOrganization getAdaptedOrganization() {
        return adaptedOrganization;
    }
    @Override
    public void beforeMarshal(Object arg0) {
        if(arg0 instanceof Organization) {
            Organization organization = (Organization) arg0;
            if(organization.getAncestors() != null) {
                for(Organization ancestorOrganization : organization.getAncestors()) {
                    if(adaptedOrganization.getOrganization() != ancestorOrganization && !adaptedOrganization.getOrphans().contains(ancestorOrganization)) {
                        adaptedOrganization.getOrphans().add(ancestorOrganization);
                    }
                }
            }
            if(organization.getDescendents() != null) {
                for(Organization descendantOrganization : organization.getDescendents()) {
                    if(adaptedOrganization.getOrganization() != descendantOrganization && !adaptedOrganization.getOrphans().contains(descendantOrganization)) {
                        adaptedOrganization.getOrphans().add(descendantOrganization);
                    }
                }
            }
        }
    }
}
 Pulling it all together, below is how we can marshal a graph of Organization objects:
 
 
 
public class Demo {
    public static void main(String[] args) throws Exception {
        Organization organization1 = new Organization();
        organization1.setId(1);
        Organization organization2 = new Organization();
        organization2.setId(2);
        organization2.getAncestors().add(organization1);
        organization1.getDescendents().add(organization2);
        Organization organization3 = new Organization();
        organization3.setId(3);
        organization3.getAncestors().add(organization2);
        organization2.getDescendents().add(organization3);
        JAXBContext jaxbContext = JAXBContext.newInstance(Organization.class, AdaptedOrganization.class);
        System.out.println(jaxbContext);
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        OrganizationMarshalListener marshalListener = new OrganizationMarshalListener(organization1);
        marshaller.setListener(marshalListener);
        marshaller.marshal(marshalListener.getAdaptedOrganization(), System.out);
    }
}
 I hope this helps, I want to add native support for this use case in an upcoming EclipseLink release.
 
 -Blaise
 |  |  |  | 
|  | 
|  | 
|  | 
|  | 
|  | 
|  | 
|  | 
|  | 
Powered by 
FUDForum. Page generated in 0.05622 seconds