6

在过去的几天里,我尝试使用 MOXy JAXB 来支持 Hibernate 模型的 XML 编组/解组。尝试这样做时,我遇到了休眠代理对象的问题。考虑类似的事情:

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

我的问题是休眠实例化了通过在用户上调用 getAddress() 获得的地址的代理对象。然后,当 JAXB 尝试封送对象时,它无法发现它实际上是它正在尝试封送的 CoolAddress,这导致 CoolAddress 中的属性没有被封送。

我已经搜索/考虑了以下可能的解决方案:

  • 以某种方式从 JAXB 获得回调,允许我将正在编组的对象替换为另一个对象。这将允许我从代理获取真实对象。
  • 触摸模型中的所有对象,这将使休眠获取真实对象。除了手动运行所有非瞬态属性之外,我还没有找到任何聪明的方法,这很乏味。
  • 设置休眠以在我正在编组模型的会话中使用急切获取。

我正在寻找替代建议,或者是否可以(并且容易)实施上述建议之一。任何帮助表示赞赏:)。

4

1 回答 1

7

要解决这个 Hibernate 问题,您可以使用XmlAdapter. XmlAdapter 看起来类似于 marshal 方法中的逻辑是从代理转换为真实对象:

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

}

酷地址

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
于 2011-07-27T15:58:56.853 回答