We'll use the following model. Notice how Customer has a relationship to Address, and Address has a relationship back to Customer. In JPA one direction of the relationship is mapped (in this case the customer property on Address), and the other direction specifies a mapping to leverage.
import javax.persistence.*; @Entity public class Customer { @Id private long id; @OneToOne(mappedBy="customer", cascade={CascadeType.ALL}) private Address address; }
import javax.persistence.*; @Entity public class Address implements Serializable { @Id private long id; @MapsId @OneToOne @JoinColumn(name="ID") private Customer customer; }
Using vanilla JAXB to marshal these objects to XML you need to mark one direction @XmlTrasient, this prevents JAXB from entering an infinite loop during marshalling (object-to-XML). During unmarshalling (XML-to-object) however you are responsible for populating the back pointer yourself.
import javax.persistence.*; import javax.xml.bind.annotation.*; @Entity public class Address implements Serializable { @Id private long id; @OneToOne @JoinColumn(name="ID") @MapsId @XmlTransient private Customer customer; }
MOXy offers an extension that will populate the back pointer for you, this is done with the @XmlInverseReference annotation. Note how @XmlInverseReference annotation leverages the same "mappedBy" concept.
import javax.persistence.*; import org.eclipse.persistence.oxm.annotations.*; @Entity public class Address implements Serializable { @Id private long id; @OneToOne @JoinColumn(name="ID") @MapsId @XmlInverseReference(mappedBy="address") private Customer customer; }
Now as I'll demonstrate below you can move your data from JPA to JAXB and back again without having to modify the data:
// Read customer from database EntityManagerFactory emf = Persistence.createEntityManagerFactory("CustomerService"); EntityManager em = emf.createEntityManager(); Customer customer = em.find(Customer.class, (long) 1); // Save customer to XML JAXBContext jc = JAXBContext.newInstance(Customer.class); Marshaller marshaller = jc.createMarshaller(); StringWriter writer = new StringWriter(); marshaller.marshal(customer, writer); // Load customer from XML Unmarshaller unmarshaller = jc.createUnmarshaller(); StringReader reader = new StringReader(writer.toString()); Customer detachedCustomer = (Customer) unmarshaller.unmarshal(reader); detachedCustomer.setLastName("Jones"); // Persist customer to database Customer mergedCustomer = em.merge(detachedCustomer); em.getTransaction().begin(); em.persist(mergedCustomer); em.getTransaction().commit(); em.close();
In future posts I'll describe other MOXy extensions used to map JPA entities to XML.
hi,, i want to ask how to show the value of XML that should be generated by @XmlInverseReference ,, because i need the value of all generated value from that variable but because using @XmlInverseReference i can't get any of xml value that i want... Any solution??
ReplyDeleteHi sby,
ReplyDeleteIn this example when you marshal a Customer that contains an Address, both Customer and Address information will appear in the XML. @XmlInverseReference comes into play during the unmarshal when it will populate the back pointer for you.
-Blaise
Hi, I have 2 Objects that have a bi-directional relation (A-B). Now I have 2 services:
ReplyDelete1: gets A with a reference to B (this works as described)
2: gets B with a reference to A (the other side around A is null)
Is there a solution for that problem?
Hi Pascal,
DeleteYou could leverage MOXy's external binding document to apply a second set of mappings. Below is an example where this strategy is leveraged to map a single object model to both the Google and Yahoo weather services:
- Mapping Objects to Multiple XML Schemas - Weather Example
-Blaise
Thanks Blaise, that's what I was looking for. Pascal
DeleteHi Blaise,
ReplyDeleteI have another problem. It works if the relation is one-to-many with concrete types. But if the children resp. the parent are Abstract Classes, the parent (annotated with @XmlInverseReference) is null.
<code>
@Entity
@Table(name = ProjectConstants.DBTABLE_BALANCEAREA)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING)
public abstract class AbstractBalanceArea extends AbstractRootPDO {
@OneToMany(fetch = FetchType.EAGER, cascade = cascadeType.ALL, mappedBy = "parent")
@XmlAnyElement(lax = true)
Set children = new HashSet();
@ManyToOne
@JoinColumn(name = "PARENTID")
@XmlTransient //@XmlInverseReference(mappedBy = "children")
AbstractBalanceArea parent;
</code>
Did i miss something or is that not possible?
Pascal
Hi Pascal,
DeleteWe currently do not support the @XmlAnyElement/@XmlInverseReference combination. I have entered the following bugs you can use to track this:
- Bug 370579 - Enhancement: support @XmlInverseReference with @XmlAnyElement
- Bug 370573 - Enhancement: support @XmlInverseReference with @XmlElementRef
-Blaise
if you use something like:
ReplyDelete@XmlType(name = "bbb")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
Wont need @XMLTransient.
When you use @XmlAccessorType(XmlAccessType.FIELD) on a JPA entity, the behaviour can vary depending upon whether or not the lazy loading for that value has been populated. I recommend explicitly mapping what you want to have happen instead of counting on a side effect that you have no control over.
Delete-Blaise
hi does it work in a similar way if i use it to serealize and deserealize my pojos in JSON? i got problems with bidirectional relations, its working fine with XML but not JSON
ReplyDeleteHi,
DeleteYes, you can use the @XmlInverseReference annotation with MOXy's native JSON binding:
- JSON Binding with EclipseLink MOXy - Twitter Example
MOXy uses the same metadata for both its XML and JSON binding:
- Binding to JSON & XML - Geocode Example
-Blaise
Hey my friend and I posted this question on stackoverflow, we were reading you blog and it was helping us a lot, but we didn't see clear examples and documentation that we could find explaining the problem we are having, if you don't mind taking a look.
ReplyDeletehttp://stackoverflow.com/questions/17005039/jax-b-xml-inverse-reference-with-many-to-many-relationship
Your blog has been really helpful and you have done a wonderful job by the way. Any help is appreciated.
I have posted an answer to your question on Stack Overflow:
Delete- http://stackoverflow.com/a/17031101/383861
Here is link to another @XmlInverseReference post that may help:
- MOXy's @XmlInverseReference is now Truly Bidirectional
Thank you for clearing things up, I really apprciate it. I think/hope we set most of the annotations correctly, I'll be taking a look after work, but this definetly helps make sense of what we weren't sure of. Thank you again.
Delete-Kevin