With JAXB, you can derive an XML representation from a set of JPA entities, when a JPA entity has an embedded ID class.
In Example 7-4, the EmployeeId is the embedded ID of the Employee class:
Example 7-4 Sample Embedded ID
@Entity
public class PhoneNumber {
@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
private Employee contact;
}
@Entity
@IdClass(EmployeeId.class)
public class Employee {
@EmbeddedId
private EmployeeId id;
@OneToMany(mappedBy="contact")
private List<PhoneNumber> contactNumber;
}
@Embeddable
public class EmployeeId {
@Column(name="E_ID")
private BigDecimal eId;
private String country;
}
For the JAXB bindings, the XML accessor type will be set to FIELD for all the model classes. This can be set as a package level JAXB annotation, as shown here:
@XmlAccessorType(XmlAccessType.FIELD) package com.example.model; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType;
Example 7-5 uses the EclipseLink extension @XmlCustomizer which extends the JAXB specification. Because the contact attribute is a bidirectional relationship, it includes the EclipseLink extension @XmlInverseReference.
Example 7-5 Using the @XmlCustomizer Annotation
@Entity
@IdClass(EmployeeId.class)
@XmlCustomizer(EmployeeCustomizer.class)
public class Employee {
@EmbeddedId
private EmployeeId id;
@OneToMany(mappedBy="contact")
@XmlInverseReference(mappedBy="contact")
private List<PhoneNumber> contactNumber;
}
To embed the content of the EmployeeId class in the complex type corresponding to the Employee class, change the XPath on the mapping for the id property to be self or . . Then specify the XPath to the XML nodes which represent the ID.
Example 7-6 Changing the XPath
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
public class EmployeeCustomizer implements DescriptorCustomizer {
public void customize(ClassDescriptor descriptor) throws Exception {
XMLCompositeObjectMapping idMapping =
(XMLCompositeObjectMapping) descriptor.getMappingForAttributeName("id");
idMapping.setXPath(".");
descriptor.addPrimaryKeyFieldName("eId/text()");
descriptor.addPrimaryKeyFieldName("country/text()");
}
}
If the target object had a single ID then we would use @XmlIDREF. Since the target object has a compound key, we will mark the field @XmlTransient, and use the EclipseLink extension @XmlCustomizer to set up the mapping.
Example 7-7 Using the @XmlTransient Annotation
@Entity
@XmlCustomizer(PhoneNumberCustomizer.class)
public class PhoneNumber {
@ManyToOne
@JoinColumns({
@JoinColumn(name="E_ID", referencedColumnName = "E_ID"),
@JoinColumn(name="E_COUNTRY", referencedColumnName = "COUNTRY")
})
@XmlTransient
private Employee contact;
}
An XMLObjectReferenceMapping will be created. The mapping will include multiple key mappings.
import org.eclipse.persistence.config.DescriptorCustomizer;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLObjectReferenceMapping;
public class PhoneNumberCustomizer implements DescriptorCustomizer {
public void customize(ClassDescriptor descriptor) throws Exception {
XMLObjectReferenceMapping contactMapping = new XMLObjectReferenceMapping();
contactMapping.setAttributeName("contact");
contactMapping.setReferenceClass(Employee.class);
contactMapping.addSourceToTargetKeyFieldAssociation("contact/@eID", "eId/text()");
contactMapping.addSourceToTargetKeyFieldAssociation("contact/@country", "country/text()");
descriptor.addMapping(contactMapping);
}
}