2012-08-03 9 views
5

Ho implementato un'API REST basata su JPA e JAXB.JPA Long @Id come JAXB @XmlID genera l'errore di convalida XSD

Ho un classi più o meno come questo (molto semplificato):

@Entity 
@XmlRootElement 
... 
public class Thing { 
    @Id 
    @GeneratedValue 
    ... 
    @XmlAttribute 
    @XmlID 
    @XmlJavaTypeAdapter(JAXBLongAdapter.class) 
    private Long id; 
    ... 
} 

Hibernate (il mio provider JPA corrente) genera numeri come il valore id, ma sono naturalmente unici per un solo tipo, cosa in questo esempio.

Ora XSD dice che xsd: id (@XmlID) è un NCString che non può essere un numero semplice quindi ho anteposto un '_' ai numeri in JAXBLongAdapter. - come '_1'

Ora il validatore schema si lamenta:

[org.xml.sax.SAXParseException: cvc-id.2: There are multiple occurrences of ID value '_1'.] 

Se ho capito bene un xsd: element ID deve avere un valore (stringa) che è univoco nel documento XML. Ma questo è molto diverso dal modo comune di usare gli ID nei database.

Cosa faccio adesso? ho pensato tre cose:

  • Creare un JAXBLongAdapter per ogni tipo con un prefisso specifico tipo?
  • Utilizzare un altro generatore di ID JPA, forse UUID? - Ma quale?
  • Interrompere l'utilizzo di @XmlID e @XmlIDREF, che crea ridondanza e problemi generali.

Sembra che ora debba modificare lo schema del database per utilizzare ID diversi. - Ma sarebbe bello se gli ID rimanessero brevi, perché compaiono negli URL.

La mia domanda: esiste un generatore di ID che sia comparabilmente veloce e globalmente unico? O c'è un altro modo per affrontarlo?

EDIT:

Questo mod funziona un pò, lasciando intatto il ID APP.

@XmlID 
@XmlAttribute(name="id") 
private String getXmlID(){ 
    return String.format("%s-%s", this.getClass().getSimpleName(), this.getId().toString()); 
} 

private void setXmlID(String xmlid){ 
    String prefix = String.format("%s-", this.getClass().getSimpleName()); 
    if(xmlid.startsWith(prefix)){ 
     this.id = Long.parseLong(xmlid.substring(prefix.length())); 
    }else{ 
     throw new IllegalArgumentException(xmlid+" does not look like "+prefix+"###"); 
    } 
} 

Spostando il JAXB annotazione dal campo alla dedicati getter/setter privati ​​per la XmlID.

+0

Vedere http://stackoverflow.com/questions/9629948/jaxb-what-should-be-returned-from-beforemarshalmarshaller-method –

risposta

2

Questo è esattamente ciò che avevo fatto per un po 'di tempo.

È possibile chiedersi cosa è in realtà @XmlID per questo oggetto dominio quando viene eseguito il marshalling?

Una volta ho pensato che @XmlID e @XmlIDREF possono risolvere il problema circolare in JAXB.

Ecco che cosa sto facendo con le mie entità JPA insieme alle annotazioni JAXB.

Non aggiungere simple JPA @Id. Questo è il cuore di JPA.

@XmlRootElement 
public class Parent { 

    @Id 
    @XmlAttribute 
    private Long id; 

    @OneToMany 
    @XmlElement(name = "child") 
    @XmlElementWrapper 
    private Collection<Child> children; 
} 


@XmlRootElement 
public class Child { 

    @XmlAttribute 
    private Long getParentId() { 
     return parent.getId(); 
    } 

    @Id 
    @XmlAttribute 
    private Long id; 

    @ManyToOne 
    @XmlTransient // for preventing infinite circular problem 
    private Parent parent; 
} 
+0

Ciao, grazie per la risposta! - Quindi hai smesso di usare le funzionalità ID e IDREF a favore dei riferimenti "manuali" per id. - Come ricolleghi gli oggetti durante unmarshal? Usi qualche tipo di fabbrica o adater? – sleeplessnerd

+0

In realtà non è necessario ricollegarli. Padre e figlio sono oggetti di dominio molto diversi e unici. Ogni risorsa JAX-RS funziona esclusivamente per genitori o figli. Poiché Parent.id e Child.id sono unici nel DB, il mio EntityManager li trova solo con quegli ID. Quando trovo il bambino con il suo id, è già impostato il campo genitore. –

2

Nota: Sono in vantaggio EclipseLink JAXB (MOXy) e un membro del gruppo di esperti JAXB (JSR-222).

COSA La risposta dovrebbe essere

La risposta dovrebbe essere quella di utilizzare il @XmlSchemaType annotazioni su entrambe le proprietà @XmlID e @XmlIDREF. Sfortunatamente il RI JAXB non sfrutta questa combinazione e EclipseLink MOXy lo sfrutta solo per @XmlID. Ho inserito il seguente bug Moxy, che potremmo correzione, se siete interessati a questo approccio:

dipendenti

package forum11791735; 

import java.util.List; 
import javax.xml.bind.annotation.*; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 

@XmlRootElement 
@XmlAccessorType(XmlAccessType.FIELD) 
public class Employee { 

    @XmlID 
    @XmlJavaTypeAdapter(JAXBLongAdapter.class) 
    @XmlSchemaType(name="long") 
    private Long id; 

    @XmlIDREF 
    @XmlSchemaType(name="long") 
    private Employee manager; 

    @XmlElement(name="report") 
    private List<Employee> reports; 

} 

aggirare

L'errore che stai vedendo è a dipende dalla convalida dello schema. È possibile disabilitare la convalida dello schema o impostare Unmarshaller su Unmarshaller per ignorare questi errori?

ALTERNATIVE

Se si utilizza @XmlID/@XmlIDREF per mappare le relazioni bidirezionali, allora potreste essere interessati a @XmlInverseReference estensione di Moxy:

+0

Ciao, grazie per la rigorosa intuizione! - Nello schema del bug report mi sembra di "perdere" le informazioni semantiche della coppia ID/IDREF nell'universo XSD? - O non c'è una semantica speciale connessa a quei tipi? La mia idea prevedeva una generazione di client più semplice per altre lingue, ma sto affermando di dubitare che ciò diventi realtà. Mi piacerebbe comunque mantenere la convalida. – sleeplessnerd

+0

Se sono disposto a rinunciare al significato ID/IDREF e reimplementarlo manualmente utilizzando un qualche tipo di proxy di riferimento in xml - esiste un modo pulito per farlo con gli strumenti standard JAXB? Probabilmente questo risolverà anche il mio problema di riferimento-a-esterno-a-xml-jpa-entità, forse .. – sleeplessnerd