Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » EclipseLink » Understanding @XmlInverseReference
Understanding @XmlInverseReference [message #549026] Fri, 23 July 2010 17:29 Go to next message
Shelli Orton is currently offline Shelli Orton
Messages: 76
Registered: September 2009
Member
Hi,

I have a parent/child relationship between two JPA entities and am using JAXB/MOXy to generate XML for them (setter methods not included for brevity):
public class Parent
{
    private String id;
    private Set<Child> childs;

    @Id 
    @Column(name = "ID", updatable=false)
    @XmlAttribute
    public String getId()
    {        
        return this.id;
    }

    // bi-directional many-to-one association to Child
    @OneToMany(mappedBy = "parentBean")
    @XmlElement
    public Set<Child> getChilds()
    {
        return this.childs;
    }
}

public class Child
{
    private String id;
    private String attribute;
    private Parent parentBean;

    @Id 
    @Column(name = "ID", updatable=false)
    @XmlAttribute
    public String getId()
    {        
        return this.id;
    }

    @Column(name = "ATTRIBUTE")
    @XmlElement
    public String getAttribute()
    {        
        return this.attribute;
    }

    // bi-directional many-to-one association to Child
    @OneToMany(mappedBy = "parentBean")
    @XmlElement
    public Parent getparentBean()
    {
        return this.parentBean;
    }
}

Ideally I would like to produce XML like this for Parent objects:
<parent id="parentOne">
    <childs>
        <child id="childOne">
            <attribute>value</attribute>
        </child>
        <child id="childTwo">
            <attribute>value</attribute>
        </child>
    </childs>
</parent>

and XML like this for Child objects:
<child id="childOne">
    <attribute>value</attribute>
    <parentBean id=parentId />
</child>

With the annotations set as above in the classes I get circular reference errors. So, I updated the Child object to use @XmlInverseReference:

// bi-directional many-to-one association to Child
@OneToMany(mappedBy = "parentBean")
@XmlElement
@XmlInverseReference(mappedBy="parentBean")
public Parent getparentBean()
{
return this.parentBean;
}

That resulted in a NPE. I moved the @XmlInverseReference to the Parent object:
    // bi-directional many-to-one association to Child
    @OneToMany(mappedBy = "parentBean")
    @XmlElement
    @XmlInverseReference(mappedBy="parentBean")
    public Set<Child> getChilds()
    {
        return this.childs;
    }

and that got rid of the NPE. However the Parent XML does not list the Child elements.
<parent id="parentOne" />


The XML for the Child object is what I expect/want.

How do I get the parent XML to display the list of child elements without refering back to the parent and creating a circular reference?

Thanks in advance!
Re: Understanding @XmlInverseReference [message #549364 is a reply to message #549026] Mon, 26 July 2010 14:12 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise Doughan
Messages: 163
Registered: July 2009
Senior Member

Hello,

I haven't been able to reproduce the NPE you are seeing, could you reply back with the version of EclipseLink you are using.

When Parent is the root object when marshalling to XML. You should annotate the getparentBean() method as follows:

    @XmlInverseReference(mappedBy="childs")
    public Parent getparentBean() 


Your JPA mappings do not appear to be correct. The getparentBean method should probably be fully annotated with something like (using a @ManyToOne instead of @OneToMany):

    // bi-directional many-to-one association to Child
    @ManyToOne
    @JoinColumn(name="ID_PARENT")
    @XmlInverseReference(mappedBy="childs")
    public Parent getparentBean()
    {
        return this.parentBean;
    }


-Blaise
Re: Understanding @XmlInverseReference [message #550288 is a reply to message #549026] Thu, 29 July 2010 18:58 Go to previous messageGo to next message
Blaise Doughan is currently offline Blaise Doughan
Messages: 163
Registered: July 2009
Senior Member

We have had some offline discussion about this issue, and for the benefit of others that find this post below is the correct setup of using @XmlInverseReference at multiple levels:

Entity A

    import java.io.Serializable;
    import javax.persistence.*;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    
    import java.util.Set;
    
    @Entity
    @XmlRootElement
    public class EntityA implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private Set<EntityB> entityBs;
    
        @Id
        @XmlAttribute
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @OneToMany(mappedBy = "entityABean")
        @XmlElement
        public Set<EntityB> getEntityBs() {
            return this.entityBs;
        }
    
        public void setEntityBs(Set<EntityB> entityBs) {
            this.entityBs = entityBs;
        }
    
    }

Entity B

   import java.io.Serializable;
    import javax.persistence.*;
    import javax.xml.bind.annotation.XmlAttribute;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    
    import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
    
    import java.util.Set;
    
    @Entity
    @XmlRootElement
    public class EntityB implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private Set<EntityC> entityCs;
        private EntityA entityABean;
    
        @Id
        @XmlAttribute
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @ManyToOne
        @JoinColumn(name = "EntityA")
        @XmlInverseReference(mappedBy = "entityBs")
        public EntityA getEntityABean() {
            return this.entityABean;
        }
    
        public void setEntityABean(EntityA entityABean) {
            this.entityABean = entityABean;
        }
    
        @OneToMany(mappedBy = "entityBBean")
        @XmlElement
        public Set<EntityC> getEntityCs() {
            return this.entityCs;
        }
    
        public void setEntityCs(Set<EntityC> entityCs) {
            this.entityCs = entityCs;
        }
    }

Entity C

    import java.io.Serializable;
    import javax.persistence.*;
    
    import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
    
    @Entity
    public class EntityC implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;
        private EntityB entityBBean;
    
        @Id
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @ManyToOne
        @JoinColumn(name = "EntityB")
        @XmlInverseReference(mappedBy = "entityCs")
        public EntityB getEntityBBean() {
            return this.entityBBean;
        }
    
        public void setEntityBBean(EntityB entityBBean) {
            this.entityBBean = entityBBean;
        }
    }

Demo

    import java.io.FileInputStream;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(EntityA.class);
    
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            FileInputStream xml = new FileInputStream("src/test/jaxb/input.xml");
            EntityA a = (EntityA) unmarshaller.unmarshal(xml);
    
            for(EntityB b : a.getEntityBs()) {
                System.out.println(b.getEntityABean());
                for(EntityC c : b.getEntityCs()) {
                    System.out.println(c.getEntityBBean());
                }
            }
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(a, System.out);
        }
    
    }

Demo - Output

    test.jaxb.EntityA@1292d26
    test.jaxb.EntityB@196c1b0
    test.jaxb.EntityB@196c1b0
    test.jaxb.EntityA@1292d26
    test.jaxb.EntityB@1e13d52
    test.jaxb.EntityB@1e13d52

input.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <entityA>
       <entityBs>
          <entityCs/>
          <entityCs/>
       </entityBs>
       <entityBs>
          <entityCs/>
          <entityCs/>
       </entityBs>
    </entityA>


Below is another example of using @XmlInverseReference with a JPA model

- http://bdoughan.blogspot.com/2010/07/jpa-entities-to-xml-bid irectional.html
Re: Understanding @XmlInverseReference [message #550300 is a reply to message #550288] Thu, 29 July 2010 20:22 Go to previous message
Shelli Orton is currently offline Shelli Orton
Messages: 76
Registered: September 2009
Member
Thanks for the help.

It appears that there is no way for me to annotate the entity classes so that I can marshall both EntityA and EntityB and have the XML include both sides of the relationship.

So, I can have these XML snippets produced:

This for EntityA:
<entityA name="entityA1">
    <entityB name="entityB1">
        <entityCs name="entityC1"/>
        <entityCs name="entityC2"/>
    </entityB>
</entityA>

and this for EntityB:
<entityB name="entityB1">
    <entityCs name="entityC1"/>
    <entityCs name="entityC2"/>
</entityB>

But not this for EntityB:
<entityB name="entityB1">
    <entityA name="entityA1"/>
    <entityCs name="entityC1"/>
    <entityCs name="entityC2"/>
</entityB>


Is that correct?
Previous Topic:EclipseLink and Hessian - IndirectList problem
Next Topic:EclipseLink 2.1 Upgrade failure
Goto Forum:
  


Current Time: Wed Sep 03 04:55:20 GMT 2014

Powered by FUDForum. Page generated in 0.01718 seconds