Подтвердить что ты не робот

Использование JAXB для передачи экземпляров подклассов в качестве суперкласса

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

В настоящее время я аннотировал свой класс 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;

Это создает схему, которая выглядит так, как будто она должна работать, но все, что видно на стороне сервера во время операции persist, является объектом Message (этот подтип полностью потерян или, по крайней мере, он не переводится обратно в соответствующий подтип). На стороне клиента, чтобы вызвать метод, я делаю это:

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

Я пытаюсь сделать это? Какую магию JAXB мне нужно использовать, чтобы переводы выполнялись так, как они должны, т.е. Обрабатывать все на Java, как если бы это сообщение содержало количество методов, но все же сохраняло всю информацию, относящуюся к подтипу?


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

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

}

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

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

4b9b3361

Ответ 1

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