使用JAXB将子类实例作为超类传递 [英] Using JAXB to pass subclass instances as superclass

查看:214
本文介绍了使用JAXB将子类实例作为超类传递的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我拥有的是一组代表消息类型的Java类(接近25)。它们都从我想要抽象的Message类继承而来。每种消息类型都会为Message超类提供的集合添加一些额外的字段。



我正在使用RESTeasy实现一些RESTful Web服务,并且想要像这样的方法:

  public response persist(Message msg){
EntityTransaction tx = em.getTransaction();
tx.begin();
尝试{
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实现Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
整数ID;
@Embedded
标头;
@Embedded
SubHeader subHeader;

然后我的子类如下所示:

 @Entity 
@XmlRootElement(name =regmessage)
@XmlAccessorType(XmlAccessType.FIELD)
公共类REGMessage扩展消息{

@XmlElement(required = true)
int statusUpdateRate;
@XmlElement(required = true)
int networkRegistrationFlag;

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

  REGMessage msg = new REGMessage(); 
//填充其字段
响应r = client.createMessage(msg);

我试过的是什么?我需要用什么样的JAXB魔法来使翻译按照他们应该的方式进行 - 即将Java中的所有内容视为一个消息,以保持方法数量不变但仍保留所有特定于子类型的信息? p>




感谢Blaise的博客指针,现在看起来它正在努力完成工作。这是我得到的,它确实有效:

  // JAXB annotations 
@XmlRootElement(name =消息)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso(REGMessage.class)
// JPA注释
@MappedSuperclass
public class Message {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@XmlAttribute
private Integer id;

私人JICDHeader标题;
private int subheader;

@XmlAnyElement
@Transient
private Object body;

今天早上我遇到的一个问题是Hibernate出现了一个关于列数不匹配的隐含错误。

  @XmlRootElement(name = regmessage)
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
公共类REGMessage扩展消息{

private int field1;
private int field2;

现在由此代码生成的唯一表是regmessage表。在RESTeasy方面:

  @Path(/ messages)
公共类MessageResource实现IMessageResource {

私人EntityManagerFactory emf;
private EntityManager em;

Logger logger = LoggerFactory.getLogger(MessageResource.class);
$ b $ public MessageResource(){
try {
emf = Persistence.createEntityManagerFactory(shepherd);
em = emf.createEntityManager();
} catch(Exception e){
e.printStackTrace();


$ b @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();

尝试{
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();




$ b

这实现了一个接口:

  @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的注释,我可能尝试在完成了对消息& REGMessage类完全退出。

解决方案

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

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

@XmlRootElement
@XmlSeeAlso(RegMessage.class)
公共抽象类消息{

整数id;

$ b

替代策略: p>

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



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

关于事务处理的注意事项



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


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.

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

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.

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

@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 looks like 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:

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?


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:

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

@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:

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

解决方案

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

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.

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:

这篇关于使用JAXB将子类实例作为超类传递的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆