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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3821936/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 03:32:14  来源:igfitidea点击:

Using JAXB to pass subclass instances as superclass

javahibernatejaxbresteasy

提问by Bret

What I have is a set of Java classes (close to 25) representing message types. They all inherit from a Message class which I'd like to be abstract. Each message type adds a few additional fields to the set provided by the Message superclass.

我拥有的是一组表示消息类型的 Java 类(接近 25 个)。它们都继承自我希望抽象的 Message 类。每种消息类型都会向 Message 超类提供的集合添加一些额外的字段。

I'm implementing some RESTful web services using RESTeasy and would like to have methods like this:

我正在使用 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();
}

instead of having 25 separate persist methods, each tailored to a particular message type.

而不是有 25 个单独的持久方法,每个方法都针对特定的消息类型量身定制。

Currently, I've annotated my Message class like this:

目前,我已经像这样注释了我的 Message 类:

@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 then 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 schema which lookslike it should work, but all that's seen on the server side during a persist operation is a Message object (the subtype is completely lost, or at least it isn't marshalled back into its proper subtype). On the client side, to invoke the method I do this:

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

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

Is what I'm attempting possible? What JAXB magic do I need to use to make the translations happen the way they should -- ie, to treat everything in Java as if it's a Message to keep the number of methods down yet still preserve all the subtype-specific information?

我正在尝试的可能吗?我需要使用什么 JAXB 魔法来使翻译按照它们应该的方式进行——即将 Java 中的所有内容都视为消息,以减少方法的数量,但仍保留所有特定于子类型的信息?



Thanks to Blaise's blog pointers, this now looks like it's on the road to working fully. Here's what I've got, and it does work:

感谢 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;

One of the problems I encountered this morning was a cryptic error from Hibernate about the number of columns being mismatched. Once I realized that "body" was being mapped into the table, I marked it transient and voila!

我今天早上遇到的问题之一是 Hibernate 出现的一个关于列数不匹配的神秘错误。一旦我意识到“身体”被映射到表格中,我就将它标记为瞬态,瞧!

@XmlRootElement(name="regmessage")
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
public class REGMessage extends Message {

    private int field1;
    private int field2;

The only table generated from this code now is the regmessage table. On the RESTeasy side:

现在从这段代码生成的唯一表是 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();
    }
}

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 work as expected, and persistence is to the subclass's table (and there is no superclass table at all).

编组和解组工作按预期进行,持久性针对子类的表(根本没有超类表)。

I did see Blaise's note about JTA, which I may attempt to bring into this mix after I finish fleshing the Message & REGMessage classes back out fully.

我确实看到了 Blaise 关于 JTA 的注释,在我完全充实 Message 和 REGMessage 类后,我可能会尝试将其引入这个组合。

采纳答案by bdoughan

Have you tried adding the following to your message class? The @XmlSeeAlso annotation will let the JAXBContext know about the subclasses.

您是否尝试将以下内容添加到您的消息类中?@XmlSeeAlso 注释将让 JAXBContext 了解子类。

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

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

    Integer id;

}

Alternate Strategy:

替代策略:

Here is a link to a strategy I have helped people use:

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

Essentially you have one message object, and multiple individual message payloads. The relationship between the message and payload is handled through a @XmlAnyElement annotation.

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

Note on Transaction Handling

交易处理注意事项

I noticed that you are handling your own transactions. Have you considered implementing your JAX-RS service as a session bean and leverage JTA for your transaction handling? For an example see:

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