Java – passing subclass instances as superclasses using JAXB
I have a set of Java classes representing message types (close to 25) They all inherit the message class that they want to abstract Each message type adds some additional fields to the collection provided by the message superclass
I am using resteasy to implement some restful web services and hope to have such a method:
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(); }
Instead of using 25 separate persistence methods
At present, I have annotated my message class as follows:
@MappedSuperclass @XmlRootElement(name = "message") public abstract class Message implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) Integer id; @Embedded Header header; @Embedded SubHeader subHeader;
My subclass looks like this:
@Entity @XmlRootElement(name="regmessage") @XmlAccessorType(XmlAccessType.FIELD) public class REGMessage extends Message { @XmlElement(required = true) int statusUpdateRate; @XmlElement(required = true) int networkRegistrationFlag;
This creates a pattern that looks like it should work, but all patterns seen on the server side during the persistence operation are a message object (subtypes are completely missing, or at least not grouped back to their correct subtypes) On the client side, call the method and I do this:
REGMessage msg = new REGMessage(); // populate its fields Response r = client.createMessage(msg);
What JAXB magic do I need to use to make translation happen the way they should - that is, treat all content in Java as messages to maintain the number of methods and still retain all subtype specific information?
Thanks to Blaise's blog guide, it seems to be working in full swing now This is what I've got, its work:
//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;
A problem I encountered this morning was an implicit error in Hibernate about column number mismatch Once I realize that the "body" is mapping to the table, I mark it as transient, look!
@XmlRootElement(name="regmessage") @XmlAccessorType(XmlAccessType.FIELD) @Entity public class REGMessage extends Message { private int field1; private int field2;
Now, the only table that this code generates is the regmessage table In terms of 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(); } }
This implements an 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 & unmarshalling works as expected. Persistence is a subclass table (there is no superclass table at all)
I did see Blaise's notes on JTA. I can try to exit completely after completing the "message & regmessage" class
Solution
Are you trying to add the following to the message class@ The xmlseealso annotation will let jaxbcontext know about subclasses
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; @XmlRootElement @XmlSeeAlso(RegMessage.class) public abstract class Message { Integer id; }
Alternative strategy:
This is a link to the strategies I help people use:
> http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html
Essentially, you have a message object and multiple individual message payloads The relationship between the message and the payload is handled through the @ xmlanyelement annotation
Transaction processing description
I noticed that you are dealing with your own transaction Do you consider implementing the jax-rs service as a session bean and using JTA for transaction processing? For example:
> http://bdoughan.blogspot.com/2010/08/creating-restful-web-service-part-45.html