(moxy) jaxb маршалинг и hibernate прокси объекты - PullRequest
6 голосов
/ 27 июля 2011

В последние пару дней я пытался сделать поддержку XML-маршалинга / демаршаллинга модели Hibernate, используя MOXy JAXB. Пытаясь сделать это, я столкнулся с проблемой с объектами прокси Hibernates. Рассмотрим что-то вроде:

public class User {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "address")
    public Address getAddress() {
        return address;
    }
}

public abstract class Address {
    // Something
}

public class CoolAddress extends Address {
    public String getSomething() {
        return something;
    }
}

Я попытался отобразить этот код с помощью MOXy JAXB следующим образом:

@XmlAccessorType(XmlAccessType.NONE)
public class User {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "address")
    @XmlElement
    public Address getAddress() {
        return address;
    }
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso(CoolAddress.class)
public abstract class Address {
    // Something
}

@XmlAccessorType(XmlAccessType.NONE)
public class CoolAddress extends Address {
    @XmlElement
    public String getSomething() {
        return something;
    }
}

Моя проблема в том, что hibernate создает экземпляр прокси-объекта с адресом, полученным путем вызова getAddress () для пользователя. Затем, когда JAXB пытается выполнить маршализацию объекта, он не может обнаружить, что это на самом деле CoolAddress, который он пытается маршалировать, в результате чего свойства в CoolAddress не маршалируются.

Я гуглил / рассмотрел следующие возможные решения:

  • В некотором роде получить обратный вызов от JAXB, позволяющий мне заменить маршалируемый объект другим. Это позволило бы мне получить реальный объект от прокси.
  • Прикоснитесь ко всем объектам в модели, которые приведут в спящий режим выборку реальных объектов. Мне не удалось найти какой-либо разумный способ сделать это, кроме ручного запуска всех неходовых свойств, что довольно утомительно.
  • Установите hibernate, чтобы использовать активную выборку в сеансе. Я маршалирую модели.

Я ищу альтернативные предложения, или если одно из вышеупомянутых предложений возможно (и легко) реализовать. Любая помощь приветствуется:).

1 Ответ

6 голосов
/ 27 июля 2011

Для решения этой проблемы Hibernate вы можете использовать XmlAdapter. XmlAdapter будет выглядеть примерно так, где логика в методе маршала заключается в преобразовании прокси в реальный объект:

package forum6838323;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class AddressAdapter extends XmlAdapter<Address, Address> {

    @Override
    public Address unmarshal(Address v) throws Exception {
        return v;
    }

    @Override
    public Address marshal(Address v) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

}

Вы настраиваете XmlAdapter следующим образом:

public class User {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "address")
    @XmlJavaTypeAdapter(AddressAdapter.class)
    public Address getAddress() {
        return address;
    }
}

Если вам нужно передать инициализированный XmlAdapter маршаллеру JAXB, вы также можете это сделать, например, см. Следующее:

<Ч />

Альтернатива с использованием EclipseLink JPA

Примечание: отложенная загрузка в EclipseLink JPA не вызывает этой проблемы:

Пользователь

package forum6838323;

import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@Entity
@Table(name="users")
@XmlRootElement
public class User  {

    private int id;
    Address address;

    @Id
    @XmlAttribute
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "address")
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

}

Адрес

package forum6838323;

import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSeeAlso;

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="TYPE", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("ADDRESS")
@XmlSeeAlso(CoolAddress.class)
public class Address {

    private int id;
    private String street;

    @Id
    @XmlAttribute
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

}

CoolAddress

package forum6838323;

import javax.persistence.*;

@Entity
@DiscriminatorValue("COOL")
public class CoolAddress extends Address {

    private String something;

    public String getSomething() {
        return something;
    }

    public void setSomething(String something) {
        this.something = something;
    }

}

Демо

package forum6838323;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Forum6838323");
        EntityManager em = emf.createEntityManager();

        User user = em.find(User.class, 2);
        System.out.println("user.address BEFORE marshal:  " + user.address);

        JAXBContext jc = JAXBContext.newInstance(User.class);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(user, System.out);

        System.out.println("user.address AFTER marshal:  " + user.address);
    }

}

выход

Из выходных данных видно, что значение адреса лениво загружается, поскольку поле является нулевым до маршала, а затем заполняется:

user.address BEFORE marshal:  null
[EL Finest]: 2011-07-27 11:47:13.118--ServerSession(23503403)--Thread(Thread[main,5,main])--Execute query ReadObjectQuery(name="Forum6838323" referenceClass=Address )
[EL Finest]: 2011-07-27 11:47:13.118--ServerSession(23503403)--Connection(10272075)--Thread(Thread[main,5,main])--Connection acquired from connection pool [default].
[EL Fine]: 2011-07-27 11:47:13.118--ServerSession(23503403)--Connection(10272075)--Thread(Thread[main,5,main])--SELECT ID, TYPE, STREET, SOMETHING FROM ADDRESS WHERE (ID = ?)
    bind => [2]
[EL Finest]: 2011-07-27 11:47:13.118--ServerSession(23503403)--Connection(10272075)--Thread(Thread[main,5,main])--Connection released to connection pool [default].
[EL Finest]: 2011-07-27 11:47:13.118--UnitOfWork(6131844)--Thread(Thread[main,5,main])--Register the existing object forum6838323.CoolAddress@109ea96
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<user id="2">
    <address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="coolAddress" id="2">
        <street>2 B Road</street>
        <something>Cool Road</something>
    </address>
</user>
user.address AFTER marshal:  forum6838323.CoolAddress@83b1b
...