Использование JAXB для передачи экземпляров подкласса как суперкласса - PullRequest
10 голосов
/ 29 сентября 2010

У меня есть набор классов Java (около 25), представляющих типы сообщений. Все они наследуются от класса Message, который я хотел бы назвать абстрактным. Каждый тип сообщения добавляет несколько дополнительных полей к набору, предоставляемому суперклассом Message.

Я реализую некоторые веб-сервисы RESTful с использованием RESTeasy и хотел бы иметь такие методы:

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 отдельных постоянных методов, каждый из которых предназначен для определенного типа сообщения.

В настоящее время я аннотировал свой класс сообщений следующим образом:

@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, как если бы это было Message, чтобы уменьшить количество методов и в то же время сохранить всю информацию, относящуюся к подтипу?


Благодаря указателям блога Блеза теперь похоже, что он находится на пути к полной работе. Вот что у меня есть, и оно работает:

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

Единственная таблица, сгенерированная из этого кода, теперь является таблицей регулярных сообщений. На стороне 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;

}

Маршаллинг и демаршаллинг работают должным образом, а постоянство - к таблице подкласса (а таблицы суперкласса вообще нет).

Я видел заметку Блейза о JTA, которую я могу попытаться внести в этот микс после того, как закончу полностью возвращать классы Message & REGMessage.

1 Ответ

8 голосов
/ 29 сентября 2010

Вы пытались добавить следующее в свой класс сообщений? Аннотация @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 в качестве сессионного компонента и используете JTA для обработки транзакций? Для примера см .:

...