2010-09-29 4 views
10

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 &.

Répondre

7

Avez-vous essayé d'ajouter ce qui suit à votre classe de message? L'annotation @XmlSeeAlso permettra au JAXBContext de connaître les sous-classes.

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

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

    Integer id; 

} 

Stratégie Autre:

Voici un lien vers une stratégie que j'ai aidé les gens utilisent:

Essentiellement, vous avez un objet de message, et charges utiles de plusieurs messages individuels. La relation entre le message et la charge utile est gérée via une annotation @XmlAnyElement.

Note sur Traitement des transactions

Je remarque que vous gérez vos propres transactions. Avez-vous envisagé d'implémenter votre service JAX-RS en tant que bean session et de tirer parti de JTA pour la gestion de vos transactions? Pour voir un exemple:

+0

Tout est parfait - je vais lui donner un coup. Merci! – Bret

+0

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

+0

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

Questions connexes