Ce que j'ai est un ensemble de classes Java (près de 25) représentant les types de message. Ils héritent tous d'une classe Message que j'aimerais être abstraite. Chaque type de message ajoute quelques champs supplémentaires à l'ensemble fourni par la superclasse Message.Utilisation de JAXB pour passer des instances de sous-classes en tant que superclasse
Je mise en œuvre des services Web RESTful utilisant RESTeasy et je voudrais avoir des méthodes comme celle-ci:
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();
}
au lieu d'avoir 25 séparé des méthodes, persistent chacun étant adapté à un type de message particulier.
Actuellement, j'ai annotés ma classe de message comme ceci:
@MappedSuperclass
@XmlRootElement(name = "message")
public abstract class Message implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Integer id;
@Embedded
Header header;
@Embedded
SubHeader subHeader;
Ma sous-classe ressemble alors à ceci:
@Entity
@XmlRootElement(name="regmessage")
@XmlAccessorType(XmlAccessType.FIELD)
public class REGMessage extends Message {
@XmlElement(required = true)
int statusUpdateRate;
@XmlElement(required = true)
int networkRegistrationFlag;
Cela crée un schéma qui ressemble comme ça devrait fonctionner, mais tout ce qui est vu du côté du serveur pendant une opération de persistance est un objet Message (le sous-type est complètement perdu, ou du moins il n'est pas ramené dans son propre sous-type). Du côté client, d'invoquer la méthode que je fais:
REGMessage msg = new REGMessage();
// populate its fields
Response r = client.createMessage(msg);
Est-ce que je tente possible? Quelle magie JAXB dois-je utiliser pour que les traductions se déroulent comme elles le devraient - c'est-à-dire, traiter tout Java comme s'il s'agissait d'un Message pour réduire le nombre de méthodes tout en conservant toutes les informations spécifiques au sous-type? Grâce aux pointeurs de blog de Blaise, il semble que nous soyons sur la bonne voie pour travailler pleinement. Voici ce que j'ai, et il FONCTIONNE:
//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;
L'un des problèmes que je rencontrais ce matin était une erreur cryptique de veille prolongée sur le nombre de colonnes étant incompatibles. Une fois que j'ai réalisé que le «corps» était en train d'être cartographié dans la table, je l'ai marqué comme transitoire et voilà!
@XmlRootElement(name="regmessage")
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class REGMessage extends Message {
private int field1;
private int field2;
La seule table générée à partir de ce code est maintenant la table regmessage. Du côté 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();
}
}
Cette implémente une interface:
@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 & travail unmarshalling comme prévu, et la persistance est à la table de la sous-classe (et il n'y a pas de table de superclasse du tout).
J'ai vu la note de Blaise à propos de JTA, que je peux essayer de mettre dans ce mix après avoir fini de remplir complètement les classes MessageMessage &.
Tout est parfait - je vais lui donner un coup. Merci! – Bret
Quelle est l'importance de définir les membres java dans la superclasse en tant qu'attributs? Les miens sont des types complexes et je vais essayer de les laisser comme tels. – Bret
Il semble être plutôt important. Le simple fait de convertir ce joli simplement et de ré-annoter mes classes existantes aboutit à des objets qui, après unmarshalling, n'obtiennent pas la partie du corps. Ces champs sont tous 0 ou null et mes champs spécifiques au type sont également perdus. – Bret