views:

123

answers:

1

I have the following entity structure (A, B, C, D are entities):

A-> one-to-many B, 
A-> one-to-many C,
B-> one-to-many D, 
C-> one-to-many D.

I want to persist entity A with hibernate but I am sending it over web service (cyclic references are eliminated). So, on the server I receive parents that “know” about the children and children don’t know about the parents and I need to link everything up again. The problem is that I need to match D with two parents - what was on the client a single D instance, on the server became two instances which have to be merged and D hadn’t been previously persisted so it doesn’t contain unique id that can be matched. I am thinking about two solutions:

1.  Call web service twice – in first call persist Ds and then call it to persist A
2.  XmlIDRef, and XmlID annotations so I don’t have to merge Ds (jaxb will do the job for me) but in that case client will have to generate unique ids for that fields and I wanted to avoid that.

How should I do it? Am I on the right track?

Btw, I am using hibernate, cxf and jaxb.

+1  A: 

Both approaches are reasonable:

Call Web Serice Twice

Some users are breaking the messages into smaller chunks so that only privately owned data is sent over the wire in a single message. References to non-privately owned data is represented as links (the links specify how to get the object from another JAX-RS service). Then you can have XmlAdapters that resolve the links (see below):

import java.net.HttpURLConnection;
import java.net.URL;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.adapters.XmlAdapter;

import org.example.product.Product;

public class ProductAdapter  extends XmlAdapter<String, Product>{

    private JAXBContext jaxbContext;

    public ProductAdapter() {
        try {
            jaxbContext = JAXBContext.newInstance(Product.class);
        } catch(JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String marshal(Product v) throws Exception {
        if(null == v) {
            return null;
        }
        return "http://localhost:9999/products/" + v.getId();
    }

    @Override
    public Product unmarshal(String v) throws Exception {
        if(null == v) {
            return null;
        }

        URL url = new URL(v);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setRequestProperty("Accept", "application/xml");

        Product product = (Product) jaxbContext.createUnmarshaller().unmarshal(connection.getInputStream());
        connection.disconnect();
        return product;
    }

}

@XmlID/@XMLIDREF

If you are going to send all the data in one call and B and C share references to instances of D, then you will need @XmlID/@XmlIDREF. You will need an object to nest the instances of D under. In this case under A would be appropriate. Below is a thread I had with a user about automating this:

Cyclic References

The MOXy JAXB implementation has extensions for handling cyclic relationships. This is done through the @XmlInverseReference annotation. For more information see:

Blaise Doughan
Thanks for the detailed answer.
draganstankovic