使用JAXB将子类实例作为超类传递

我所拥有的是一组表示消息类型的Java类(接近25个)。 它们都inheritance自我想要抽象的Message类。 每种消息类型都会向Message超类提供的集合中添加一些其他字段。

我正在使用RESTeasy实现一些RESTful Web服务,并希望有这样的方法:

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

而不是有25个单独的持久化方法,每个方法都适合特定的消息类型。

目前,我已经注释了我的Message类,如下所示:

 @MappedSuperclass @XmlRootElement(name = "message") public abstract class Message implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) Integer id; @Embedded Header header; @Embedded SubHeader subHeader; 

我的子类看起来像这样:

 @Entity @XmlRootElement(name="regmessage") @XmlAccessorType(XmlAccessType.FIELD) public class REGMessage extends Message { @XmlElement(required = true) int statusUpdateRate; @XmlElement(required = true) int networkRegistrationFlag; 

这会创建一个看起来应该工作的模式,但是在持久化操作期间在服务器端看到的所有内容都是Message对象(子类型完全丢失,或者至少它没有被编组回其正确的子类型)。 在客户端,要调用方法,我这样做:

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

我正在尝试的是什么? 我需要使用什么JAXB魔术才能使翻译按照应有的方式发生 – 也就是说,将Java中的所有内容视为一条消息,以保持方法数量的减少但仍然保留所有特定于子类型的信息?


感谢Blaise的博客指针,现在看起来它正在完全工作的道路上。 这就是我所拥有的,它确实有效:

 //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; 

我今天早上遇到的一个问题是来自Hibernate的关于列数不匹配的神秘错误。 一旦我意识到“身体”被映射到桌子上,我就把它标记为瞬态而且瞧!

 @XmlRootElement(name="regmessage") @XmlAccessorType(XmlAccessType.FIELD) @Entity public class REGMessage extends Message { private int field1; private int field2; 

现在,此代码生成的唯一表是regmessage表。 在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(); } } 

这实现了一个接口:

 @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; } 

编组和解组按预期工作,持久性是子类的表(并且根本没有超类表)。

我确实看到了Blaise关于JTA的注释,在完成对Message&REGMessage类的完全填充后,我可能会尝试将其添加到这个混合中。

您是否尝试将以下内容添加到邮件类中? @XmlSeeAlso注释将让JAXBContext知道子类。

 import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; @XmlRootElement @XmlSeeAlso(RegMessage.class) public abstract class Message { Integer id; } 

替代战略:

这是我帮助人们使用的策略的链接:

基本上,您有一个消息对象和多个单独的消息有效负载。 消息和有效负载之间的关系通过@XmlAnyElement注释处理。

关于交易处理的说明

我注意到你正在处理自己的交易。 您是否考虑将JAX-RS服务实现为会话bean并利用JTA进行事务处理? 有关示例,请参阅: