2010-09-29 23 views
10

Lo que tengo es un conjunto de clases de Java (cerca de 25) que representan tipos de mensajes. Todos ellos heredan de una clase de mensaje que me gustaría ser abstracto. Cada tipo de mensaje agrega algunos campos adicionales al conjunto proporcionado por la superclase Mensaje.Usar JAXB para pasar instancias de subclase como superclase

estoy poniendo en práctica algunos servicios web REST utilizando RESTeasy y me gustaría tener métodos como éste:

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(); 
} 

en lugar de tener 25 separada persisten métodos, cada uno adaptado a un determinado tipo de mensaje.

Actualmente, he anotada clase de mi mensaje como este:

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

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

Mi subclase tendrá el aspecto siguiente:

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

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

Esto crea un esquema de lo que se ve que debería funcionar, pero todo lo que se ve en el lado del servidor durante una operación persistente es un objeto Message (el subtipo se pierde por completo, o al menos no se reorganiza en su subtipo correcto). En el lado del cliente, para invocar el método hago esto:

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

¿Es lo que estoy intentando posible? ¿Qué magia JAXB necesito usar para que las traducciones sucedan de la manera que deberían, es decir, tratar todo en Java como si fuera un Mensaje para mantener el número de métodos abajo y aún así conservar toda la información específica del subtipo?


Gracias a los indicadores del blog de Blaise, ahora parece que está en el camino correcto para funcionar. Esto es lo que tengo, y lo hace el trabajo:

//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 de los problemas que tuve esta mañana fue un error críptico de Hibernate sobre el número de columnas que son coincidentes. Una vez que me di cuenta de que el "cuerpo" estaba siendo mapeado en la mesa, lo marqué de manera pasajera y ¡voilá!

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

    private int field1; 
    private int field2; 

La única tabla generada a partir de este código ahora es la tabla de regsage. En el lado 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(); 
    } 
} 

Esto implementa una interfaz:

@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 & trabajo unmarshalling como se esperaba, y la persistencia es la mesa de la subclase (y no hay una mesa superclase en absoluto).

Vi la nota de Blaise sobre JTA, que puedo tratar de incluir en esta mezcla después de terminar de procesar las clases REGMessage del mensaje & completamente fuera de servicio.

Respuesta

7

¿Ha intentado agregar lo siguiente a su clase de mensaje? La anotación @XmlSeeAlso le permitirá al JAXBContext conocer las subclases.

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

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

    Integer id; 

} 

estrategia alternativa:

Aquí hay un enlace a una estrategia que he ayudado a la gente usa:

Esencialmente, usted tiene un objeto de mensaje y múltiples cargas útiles de mensajes individuales. La relación entre el mensaje y la carga se maneja a través de una anotación @XmlAnyElement.

Nota sobre la manipulación de transacciones

me di cuenta de que usted está manejando sus propias transacciones. ¿Ha considerado implementar su servicio JAX-RS como un bean de sesión y aprovechar JTA para el manejo de sus transacciones? Para ver un ejemplo:

+0

Eso se ve perfecto - Voy a darle una oportunidad. ¡Gracias! – Bret

+0

¿Cuán importante es que los miembros java de la superclase se definan como atributos? Los míos son tipos complejos y trataré de dejarlos como tales. – Bret

+0

Parece ser bastante importante. Simplemente al convertir esto de forma sencilla y volver a anotar las clases existentes, se obtienen objetos que, al desasociarse, no obtienen la parte del cuerpo. Esos campos son 0 o nulos y mis campos específicos de tipo también se están perdiendo. – Bret

Cuestiones relacionadas