viernes, 6 de septiembre de 2013

JAXB. Realizar el mapeo a XML con relaciones cruzadas. El error com.sun.istack.internal.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML

Si habéis seguido los ejemplos anteriores sobre JAXB (aquí, aquí, aquí y aquí), tenemos creadas una clase Cliente y una clase Direccion relacionadas entre si, donde uno de los atributos de la clase Direccion es un Cliente, de modo que podemos tener identificadas las múltiples direcciones de un cliente almacenadas en base de datos. Además creamos XML a partir de una lista de direcciones y creamos una lista de objetos Direccion mediante un fichero XML empleando en ambos casos una clase intermedia que llamamos DireccionResponse que nos permitía pasar al Marshall un solo objeto aunque este tenga en su interior una lista.
Pero ahora vamos a crear un nuevo método en la clase Cliente que nos permita acceder directamente a todas las direcciones. Para ello tenemos que añadir a la Entity Cliente tanto el atributo con la lista de direcciones como los método get y set. Por lo tanto a Cliente.java tenemos que añadirle lo siguiente:
protected List<Direccion> direcciones = new ArrayList<Direccion>(0);
@OneToMany(fetch = FetchType.LAZY, mappedBy="cliente")
public List<Direccion> getDirecciones() {
       return direcciones;
}
public void setDirecciones(List<Direccion> direcciones) {
      this.direcciones = direcciones;
}
Que sucede entonces si compilamos?? Que obtenemos un error del tipo [com.sun.istack.internal.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML. Esto se debe a que al tener un método getDirecciones, estamos entrando en un bucle infinito. Es decir, en el XML hemos visto como se muestra también el cliente al que pertenece cada dirección, pero ahora además también cargamos de nuevo en el cliente la dirección y así sucesivamente. Por lo que tenemos que romper dicho bucle. Para ello tenemos que emplear la anotación @XmlAccessorType que nos indica que campos serán serializados en el XML por defecto. En este caso, indicaremos que ninguna de las propiedades sea serializada por defecto empleando @XmlAccessorType(XmlAccessType.NONE) y entonces tendremos que marcar todos los elementos que queremos que aparezcan con @XmlElement. De este modo, nuestra clase Cliente.java quedará de la siguiente forma:
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@Entity
@SequenceGenerator(
    name="cliente_seq",
    sequenceName="cliente_seq",
    initialValue=1,
    allocationSize=1
)

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@Table(name = "cliente")
public class Cliente {
   
    private static final long serialVersionUID = 1L;
   
    @XmlElement
    protected long id;
   
    @XmlElement   
    protected String razonSocial;
   
    @XmlElement
    protected String cif;
   
    @XmlElement
    protected String direccion;
   
    @XmlElement
    protected String codPostal;
   
    @XmlElement
    protected String localidad;
   
    @XmlElement
    protected String pais;
   
    @XmlElement
    protected String telf1;
   
    @XmlElement
    protected String telf2;
   
    @XmlElement
    protected String email1;
   
    @XmlElement
    protected String email2;
   
    @XmlElement
    protected String fax;
   
    protected List<Direccion> direcciones = new ArrayList<Direccion>(0);

    public Cliente() {
        super();
    }
   
    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="cliente_seq")
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
   
    public String getDireccion() {
        return direccion;
    }

    public void setDireccion(String direccion) {
        this.direccion = direccion;
    }

    public String getCodPostal() {
        return codPostal;
    }

    public void setCodPostal(String codPostal) {
        this.codPostal = codPostal;
    }

    public String getLocalidad() {
        return localidad;
    }

    public void setLocalidad(String localidad) {
        this.localidad = localidad;
    }

    public String getPais() {
        return pais;
    }

    public void setPais(String pais) {
        this.pais = pais;
    }

    public String getTelf1() {
        return telf1;
    }

    public void setTelf1(String telf1) {
        this.telf1 = telf1;
    }

    public String getTelf2() {
        return telf2;
    }

    public void setTelf2(String telf2) {
        this.telf2 = telf2;
    }

    public String getEmail1() {
        return email1;
    }

    public void setEmail1(String email1) {
        this.email1 = email1;
    }

    public String getEmail2() {
        return email2;
    }

    public void setEmail2(String email2) {
        this.email2 = email2;
    }

    public String getRazonSocial() {
        return razonSocial;
    }

    public void setRazonSocial(String razonSocial) {
        this.razonSocial = razonSocial;
    }

    public String getCif() {
        return cif;
    }

    public void setCif(String cif) {
        this.cif = cif;
    }

    public String getFax() {
        return fax;
    }

    public void setFax(String fax) {
        this.fax = fax;
    }   
   
    @OneToMany(fetch = FetchType.LAZY, mappedBy="cliente")
    public List<Direccion> getDirecciones() {
        return direcciones;
    }
    public void setDirecciones(List<Direccion> direcciones) {
        this.direcciones = direcciones;
    }
}

No hay comentarios :

Publicar un comentario