2010-09-29 12 views
10

Quello che ho è un insieme di classi Java (vicino a 25) che rappresentano i tipi di messaggio. Tutti ereditano da una classe di messaggi che mi piacerebbe essere astratta. Ogni tipo di messaggio aggiunge alcuni campi aggiuntivi all'insieme fornito dalla superclasse del messaggio.Utilizzo di JAXB per passare le istanze delle sottoclassi come superclasse

sto implementando alcuni servizi web RESTful utilizzando RESTEasy e vorrei avere metodi come questo:

public Response persist(Message msg) { 
    EntityTransaction tx = em.getTransaction(); 
    tx.begin(); 
    try { 
     em.persist(msg); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    tx.commit(); 
    em.close(); 
    return Response.created(URI.create("/message/" + msg.getId())).build(); 
} 

invece di avere 25 separata persistono metodi, ciascuno su misura per un particolare tipo di messaggio.

Attualmente, ho annotato classe il mio messaggio come questo:

@MappedSuperclass 
@XmlRootElement(name = "message") 
public abstract class Message implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    Integer id; 
    @Embedded 
    Header header; 
    @Embedded 
    SubHeader subHeader; 

mio sottoclasse sembra poi così:

@Entity 
@XmlRootElement(name="regmessage") 
@XmlAccessorType(XmlAccessType.FIELD) 
public class REGMessage extends Message { 

    @XmlElement(required = true) 
    int statusUpdateRate; 
    @XmlElement(required = true) 
    int networkRegistrationFlag; 

Questo crea uno schema che sembra come dovrebbe funzionare, ma tutto ciò che viene visto sul lato server durante un'operazione persistente è un oggetto Message (il sottotipo è completamente perso, o almeno non è stato eseguito il marshalling nel sottotipo corretto). Sul lato client, per invocare il metodo faccio questo:

REGMessage msg = new REGMessage(); 
// populate its fields 
Response r = client.createMessage(msg); 

è quello che sto tentando possibile? Quale magia JAXB ho bisogno di usare per fare in modo che le traduzioni avvengano come dovrebbero - cioè, per trattare tutto in Java come se fosse un messaggio per mantenere il numero di metodi in basso e tuttavia conservare tutte le informazioni specifiche del sottotipo?


Grazie ai puntatori del blog di Blaise, ora sembra che sia sulla strada per lavorare pienamente. Ecco quello che ho, e lo fa di lavoro:

//JAXB annotations 
@XmlRootElement(name="message") 
@XmlAccessorType(XmlAccessType.FIELD) 
@XmlSeeAlso(REGMessage.class) 
//JPA annotations 
@MappedSuperclass 
public class Message { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    @XmlAttribute 
    private Integer id; 

    private JICDHeader header; 
    private int subheader; 

    @XmlAnyElement 
    @Transient 
    private Object body; 

Uno dei problemi che ho incontrato questa mattina è stato un errore criptico da Hibernate circa il numero di colonne che sono non corrispondenti. Una volta che ho realizzato che il "corpo" veniva mappato sul tavolo, l'ho contrassegnato come transitorio e voilà!

@XmlRootElement(name="regmessage") 
@XmlAccessorType(XmlAccessType.FIELD) 
@Entity 
public class REGMessage extends Message { 

    private int field1; 
    private int field2; 

L'unica tabella generata da questo codice ora è la tabella regmessage. Sul lato RESTEasy:

@Path("/messages") 
public class MessageResource implements IMessageResource { 

    private EntityManagerFactory emf; 
    private EntityManager em; 

    Logger logger = LoggerFactory.getLogger(MessageResource.class); 

    public MessageResource() { 
     try { 
      emf = Persistence.createEntityManagerFactory("shepherd"); 
      em = emf.createEntityManager(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    @Override 
    @POST 
    @Consumes("application/xml") 
    public Response saveMessage(Message msg) { 

     System.out.println(msg.toString()); 

     logger.info("starting saveMessage"); 
     EntityTransaction tx = em.getTransaction(); 
     tx.begin(); 

     try { 
      em.persist(msg); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     tx.commit(); 
     em.close(); 
     logger.info("ending saveMessage"); 

     return Response.created(URI.create("/message/" + msg.getId())).build(); 
    } 
} 

Questa implementa un'interfaccia:

@Path("/messages") 
public interface IMessageResource { 

    @GET 
    @Produces("application/xml") 
    @Path("{id}") 
    public Message getMessage(@PathParam("id") int id); 

    @POST 
    @Consumes("application/xml") 
    public Response saveMessage(Message msg) throws URISyntaxException; 

} 

Marshalling & lavoro unmarshalling come previsto, e la persistenza è al tavolo del sottoclasse (e non v'è alcuna tabella superclasse a tutti).

ho fatto vedere la nota di Blaise su JTA, che io possa tentare di portare in questo mix dopo che ho finito di ideare le classi REGMessage Messaggio & indietro completamente fuori.

risposta

7

Hai provato ad aggiungere quanto segue alla classe del messaggio? L'annotazione @XmlSeeAlso consentirà a JAXBContext di conoscere le sottoclassi.

import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.XmlSeeAlso; 

@XmlRootElement 
@XmlSeeAlso(RegMessage.class) 
public abstract class Message { 

    Integer id; 

} 

strategia alternativa:

Ecco un link ad una strategia che ho aiutato la gente usa:

In sostanza si ha un oggetto messaggio e più payload di messaggi individuali. La relazione tra il messaggio e il carico utile viene gestita tramite un'annotazione @XmlAnyElement.

Nota sulla transazione Handling

ho notato che si sta gestendo le proprie transazioni. Avete considerato di implementare il vostro servizio JAX-RS come bean di sessione e sfruttare JTA per la gestione delle transazioni? Per un esempio vedere:

+0

che sembra perfetto - ho deciso di dargli un andare. Grazie! – Bret

+0

Quanto è importante che i membri java nella superclasse siano definiti come attributi? I miei sono tipi complessi e cercherò di lasciarli come tali. – Bret

+0

Sembra piuttosto importante. Basta convertire questo in modo piuttosto semplice e ri-annotando le mie classi esistenti si traducono in oggetti che, dopo averli eliminati, non ottengono la parte del corpo. Questi campi sono tutti 0 o null e anche i miei campi specifici del tipo vengono persi. – Bret

Problemi correlati