在过去的几天中,我尝试使用MOXy JAXB支持对Hibernate模型的XML编组/解组。尝试执行此操作时,我遇到了hibernate代理对象的问题。考虑类似:
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实例化了通过在User上调用getAddress()获得的地址的代理对象。然后,当JAXB尝试封送对象时,它无法发现它实际上是它要封送的CoolAddress,这导致CoolAddress中的属性未被封送。
我已经搜寻/考虑了以下可能的解决方案:
我正在寻找替代建议,或者上述建议之一是否可能(且很容易实现)。任何帮助表示赞赏:)。
要解决此Hibernate问题,您可以使用XmlAdapter。XmlAdapter看起来类似于marshal方法中的逻辑将从代理转换为真实对象的地方:
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; } }
使用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); } }
输出量
您可以从输出中看到地址值被延迟加载,因为该字段在编组之前为null,然后在该字段之后填充:
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