Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/MOXy/Relationships/Shared Reference/Keys and Foreign Keys/Composite Key"

m
Line 9: Line 9:
 
}}  
 
}}  
  
= Using Composite (Compound) Primary Keys =
+
=Mapping Single Key Relationships=
With JAXB, you can generate XML from a set of JPA entities with composite (compound) primary keys.
+
  
For example, consider the following example:
+
To model non-privately-owned relationships, your "target" objects must have IDs (keys) defined, and your "source" object must use these IDs to map the relationship.
  
<source lang="java">
+
Relationships represented with keys use the '''@XmlID''' and '''@XmlIDREF''' annotations.  Although the JAXB specification requires that the property marked with '''@XmlID''' be a '''String''', MOXy JAXB does not enforce this restriction.
@Entity
+
public class PhoneNumber {
+
  
    @ManyToOne
+
In this example, each '''Employee''' has one '''manager''' but multiple '''reports'''.
    @JoinColumns({
+
        @JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
+
        @JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
+
    })
+
  
    private Employee contact;
+
<source lang="java">
 
+
@XmlAccessorType(XmlAccessType.FIELD)
}
+
 
+
@Entity
+
@IdClass(EmployeeId.class)
+
 
public class Employee {
 
public class Employee {
 +
    @XmlAttribute
 +
    @XmlID
 +
    private Integer id;
 +
 +
    @XmlAttribute
 +
    private String name;
 +
 +
    @XmlIDREF
 +
    private Employee manager;
 +
 +
    @XmlElement(name="report")
 +
    @XmlIDREF
 +
    private List<Employee> reports;
  
     @Id
+
     ...
    @Column(name="E_ID")
+
    private BigDecimal eId;
+
 
+
    @Id
+
    private String country;
+
 
+
    @OneToMany(mappedBy="contact")
+
    private List<PhoneNumber> contactNumber;
+
 
+
}
+
 
+
public class EmployeeId {
+
 
+
    private BigDecimal eId;
+
    private String country;
+
 
+
 
}
 
}
 
</source>
 
</source>
  
In this example, set the '''XmlAccessType''' to '''FIELD''' for each model class. You can configure this as a package-level JAXB annotation, as shown here:
+
The following example shows how to define this mapping information in EclipseLink's OXM metadata format.
  
<source lang="java">
+
<source lang="xml">
@XmlAccessorType(XmlAccessType.FIELD)
+
...
package com.example.model;
+
<java-type name="Employee">
 
+
  <java-attributes>
import javax.xml.bind.annotation.XmlAccessType;
+
      <xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/>
import javax.xml.bind.annotation.XmlAccessorType;
+
      <xml-attribute java-attribute="name" type="java.lang.String"/>
 +
      <xml-element java-attribute="manager" type="mypackage.Employee" xml-idref="true"/>
 +
      <xml-element java-attribute="reports" type="mypackage.Employee" container-type="java.util.ArrayList" xml-idref="true"/>
 +
  </java-attributes>
 +
</java-type>
 +
...
 
</source>
 
</source>
  
=== Target Object ===
+
This would produce the following XML:
You can use the EclispeLink '''@XmlCustomizer''' for configuration options beyond the JAXB specification. Because the relationship is bidirectional, you must use the '''@XmlInverseReference''' extension, as shown here:
+
  
<source lang="java">
+
<source lang="xml">
@Entity
+
<company>
@IdClass(EmployeeId.class)
+
    <employee id="1" name="Jane Doe">
@XmlCustomizer(EmployeeCustomizer.class)
+
        <report>2</report>
public class Employee {
+
        <report>3</report>
 
+
     </employee>
     @Id
+
     <employee id="2" name="John Smith">
     @Column(name="E_ID")
+
        <manager>1</manager>
    private BigDecimal eId;
+
     </employee>
 
+
     <employee id="3" name="Anne Jones">
     @Id
+
        <manager>1</manager>
     private String country;
+
     </employee>
 
+
</company>
    @OneToMany(mappedBy="contact")
+
    @XmlInverseReference(mappedBy="contact")
+
     private List<PhoneNumber> contactNumber;
+
 
+
}
+
 
</source>
 
</source>
  
You must also specify the XPath to the XML nodes which represent the ID:
+
The '''manager''' and '''reports''' elements contain the IDs of the '''Employee''' instances they are referencing.
  
<source lang="java">
 
import org.eclipse.persistence.config.DescriptorCustomizer;
 
import org.eclipse.persistence.descriptors.ClassDescriptor;
 
  
public class EmployeeCustomizer implements DescriptorCustomizer {
+
== Using @XmlList ==
  
    public void customize(ClassDescriptor descriptor) throws Exception {
+
Because the '''@XmlIDREF''' annotation is also compatible with the '''@XmlList''' annotation, the '''Employee''' object could be modeled as:
        descriptor.addPrimaryKeyFieldName("eId/text()");
+
        descriptor.addPrimaryKeyFieldName("country/text()");
+
    }
+
 
+
}
+
</source>
+
 
+
=== Source Object ===
+
 
+
If the target object had a single ID then we would use '''[[EclipseLink/UserGuide/MOXy/Relationships/Shared_Reference/Keys_and_Foreign_Keys/Single_Key|@XmlIDREF]]'''.  Because the target object has a ''compound key'', use the '''@XmlTransient''' annotation.
+
 
+
Use the EclipseLink '''@XmlCustomizer''' extension to create the mapping as shown here.
+
  
 
<source lang="java">
 
<source lang="java">
@Entity
+
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(PhoneNumberCustomizer.class)
+
public class Employee {
public class PhoneNumber {
+
     @XmlID
 
+
     @XmlAttribute
     @ManyToOne
+
    private Integer id;
     @JoinColumns({
+
        @JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
+
    @XmlAttribute
        @JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
+
    private String name;
     })
+
     @XmlTransient
+
    @XmlIDREF
     private Employee contact;
+
     private Employee manager;
 +
 +
    @XmlIDREF
 +
     @XmlList
 +
     private List<Employee> reports;
  
 +
    ...
 
}
 
}
</source>
+
</source>  
  
EclipseLink creates the '''XMLObjectReferenceMapping'''.  This mapping includes multiple key mappings.
+
This would produce the following XML:
 
+
<source lang="xml">
<source lang="java">
+
<company>
import org.eclipse.persistence.config.DescriptorCustomizer;
+
  <employee id="1" name="Jane Doe">
import org.eclipse.persistence.descriptors.ClassDescriptor;
+
      <reports>2 3</reports>
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;
+
  </employee>
 
+
  <employee id="2" name="John Smith">
public class PhoneNumberCustomizer implements DescriptorCustomizer {
+
      <manager>1</manager>
 
+
  </employee>
    public void customize(ClassDescriptor descriptor) throws Exception {
+
  <employee id="3" name="Anne Jones">
        XMLObjectReferenceMapping contactMapping = new XMLObjectReferenceMapping();
+
      <manager>1</manager>
        contactMapping.setAttributeName("contact");
+
  </employee>
        contactMapping.setReferenceClass(Employee.class);
+
</company>
        contactMapping.addSourceToTargetKeyFieldAssociation("contact/@eID", "eId/text()");
+
        contactMapping.addSourceToTargetKeyFieldAssociation("contact/@country", "country/text()");
+
        descriptor.addMapping(contactMapping);
+
    }
+
 
+
}
+
 
</source>
 
</source>
 
 
  
 
{{EclipseLink_MOXy
 
{{EclipseLink_MOXy

Revision as of 15:39, 16 March 2011

EclipseLink MOXy


Mapping Single Key Relationships

To model non-privately-owned relationships, your "target" objects must have IDs (keys) defined, and your "source" object must use these IDs to map the relationship.

Relationships represented with keys use the @XmlID and @XmlIDREF annotations. Although the JAXB specification requires that the property marked with @XmlID be a String, MOXy JAXB does not enforce this restriction.

In this example, each Employee has one manager but multiple reports.

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
    @XmlAttribute
    @XmlID
    private Integer id;
 
    @XmlAttribute
    private String name;
 
    @XmlIDREF
    private Employee manager;
 
    @XmlElement(name="report")
    @XmlIDREF
    private List<Employee> reports;
 
    ...
}

The following example shows how to define this mapping information in EclipseLink's OXM metadata format.

...
<java-type name="Employee">
   <java-attributes>
      <xml-attribute java-attribute="id" type="java.lang.Integer" xml-id="true"/>
      <xml-attribute java-attribute="name" type="java.lang.String"/>
      <xml-element java-attribute="manager" type="mypackage.Employee" xml-idref="true"/>
      <xml-element java-attribute="reports" type="mypackage.Employee" container-type="java.util.ArrayList" xml-idref="true"/>
   </java-attributes>
</java-type>
...

This would produce the following XML:

<company>
    <employee id="1" name="Jane Doe">
        <report>2</report>
        <report>3</report>
    </employee>
    <employee id="2" name="John Smith">
        <manager>1</manager>
    </employee>
    <employee id="3" name="Anne Jones">
        <manager>1</manager>
    </employee>
</company>

The manager and reports elements contain the IDs of the Employee instances they are referencing.


Using @XmlList

Because the @XmlIDREF annotation is also compatible with the @XmlList annotation, the Employee object could be modeled as:

@XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
    @XmlID
    @XmlAttribute
    private Integer id;
 
    @XmlAttribute
    private String name;
 
    @XmlIDREF
    private Employee manager;
 
    @XmlIDREF
    @XmlList
    private List<Employee> reports;
 
    ... 
}

This would produce the following XML:

<company>
   <employee id="1" name="Jane Doe">
      <reports>2 3</reports>
   </employee>
   <employee id="2" name="John Smith">
      <manager>1</manager>
   </employee>
   <employee id="3" name="Anne Jones">
      <manager>1</manager>
   </employee>
</company>

Eclipselink-logo.gif
Version: 2.2.0 DRAFT
Other versions...

Copyright © Eclipse Foundation, Inc. All Rights Reserved.