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.
Eso se ve perfecto - Voy a darle una oportunidad. ¡Gracias! – Bret
¿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
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