(moxy)jaxb编组和hibernate代理对象 [英] (moxy) jaxb marshaling and hibernate proxy objects
问题描述
在过去的几天里,我试图使用MOXy JAXB来支持Hibernate模型的XML编组/解组。试图做到这一点,我遇到了休眠代理对象的问题。
考虑类似于:
public class User {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name =address)
public Address getAddress(){
return address;
公共抽象类地址{
// Something
}
公共类CoolAddress extends地址{
public String getSomething(){
return something;
}
}
我试图用MOXy JAXB以下方式:
$ pre $ @ $ $ $ $ $ $ $ $ $ $ $ $ $ @ $ @ $ @ $($ X $ A $ fetch = FetchType.LAZY)
@JoinColumn(name =address)
@XmlElement
public Address getAddress(){
return address;
$ b @XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso(CoolAddress.class)
public abstract class地址{
/ / Something
}
@XmlAccessorType(XmlAccessType.NONE)
public class CoolAddress extends Address {
@XmlElement
public String getSomething(){
返回一些东西;
$ b我的问题是,hibernate实例化一个代理对象通过调用用户的getAddress()获得的地址。然后,当JAXB尝试编组对象时,它不能发现它实际上是一个正在尝试编组的CoolAddress,这会导致CoolAddress中的属性不被封送。
我已经搜索了/考虑了以下可能的解决方案:
- 以某种方式从JAXB获取回调,允许我替换对象被另一个封送。这将允许我从代理获取真实对象。
- 触摸模型中的所有对象,这些对象将使冬眠获取真实对象。除了手动运行所有非瞬态属性外,我还没有找到任何明智的方法,这是非常乏味的。
- 将hibernate设置为在会话中使用预先抓取
我正在寻找其他建议,或者如果上述建议中的一个可能(并且容易)实施。为了解决这个Hibernate问题,你可以使用来解决这个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地址编组(地址v)抛出异常{
// TODO自动生成的方法存根
返回null;
}
}
您可以配置 XmlAdapter
如下:
public class User {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name =address)
@XmlJavaTypeAdapter(AddressAdapter.class)
public Address getAddress(){
return address;
$ b $ p
$ b 如果你需要将一个初始化的XmlAdapter传递给JAXB marshaller,你也可以这样做,参见下面的例子:
- 使用JAXB从两个XML文件交叉引用XmlIDs
另选使用EclipseLink JPA
注意:EclipseLink JPA中的延迟加载不会导致此问题:
用户
包论坛6838323;
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;
地址;
@Id
@XmlAttribute
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
$ b @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name =address)
public Address getAddress(){
return地址;
}
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;
私人字符串街道;
@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)
公共类CoolAddress扩展地址{
私有字符串;
public String getSomething(){
return something;
}
public void setSomething(String something){
this.something = something;
}
}
演示
package forum6838323;
导入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元帅:null
[EL Finest]:2011-07-27 11:47:13.118 - ServerSession(23503403) - 线程(Thread [main,5,main]) - 执行查询ReadObjectQuery(name =Forum6838323referenceClass = Address)
[EL Finest]:2011-07-27 11:47:13.118 - ServerSession(23503403 ) - 连接(10272075) - 线程(线程[main,5,main]) - 从连接池获取的连接[默认值]。
[EL Fine]:2011-07-27 11:47:13.118 - ServerSession(23503403) - 连接(10272075) - 线程(线程[main,5,main]) - SELECT ID,TYPE ,STREET,从地址所在的地方(ID =?)
bind => [2]
[EL Finest]:2011-07-27 11:47:13.118 - ServerSession(23503403) - 连接(10272075) - 线程(线程[main,5,main]) - 连接释放到连接池[默认]。
[EL Finest]:2011-07-27 11:47:13.118 - UnitOfWork(6131844) - 线程(线程[main,5,main]) - 注册现有对象论坛6838323.CoolAddress@109ea96
<?xml version =1.0encoding =UTF-8standalone =yes?>
< user id =2>
< address xmlns:xsi =http://www.w3.org/2001/XMLSchema-instancexsi:type =coolAddressid =2>
< street> 2 B路< / street>
< something>酷路< / something>
< / address>
< / user>
user.address AFTER marshal:forum6838323.CoolAddress@83b1b
In the last couple of days I have tried to make support for XML marshalling/unmarshalling of a Hibernate model, using MOXy JAXB. Trying to do this, I have run into a problem with hibernates proxy objects.
Consider something like:
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;
}
}
I have tried to map this code using MOXy JAXB in the following way:
@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;
}
}
My problem is that hibernate instantiates a proxy object of the address obtained by calling getAddress() on a User. Then, when JAXB tries to marshal the object, it can't find out that it actually is a CoolAddress it is trying to marshal, which results in that properties in CoolAddress not being marshaled.
I have googled/considered the following possible solutions:
- In some way get a callback from JAXB, allowing me to replace the object being marshaled with another. This would allow me to obtain the real object from the proxy.
- Touch all objects in the model which will make hibernate fetch the real objects. I have not been able to find any smart way doing this other than manually running through all non-transient properties, which is quiet tedious.
- Set hibernate to use eager fetching in the session I am marshaling the models.
I'm looking for alternative suggestions, or if one of the above suggestions is possible (and easy) to implement. Any help is appreciated :).
解决方案 To solve this Hibernate issue you may be able to use an XmlAdapter
. The XmlAdapter would look something like where the logic in the marshal method is to convert from the proxy to the real object:
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;
}
}
You configure the XmlAdapter
as follows:
public class User {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "address")
@XmlJavaTypeAdapter(AddressAdapter.class)
public Address getAddress() {
return address;
}
}
If you need to pass an initialized XmlAdapter to the JAXB marshaller, you can do that as well, see the following for an example:
Alternative Using EclipseLink JPA
Note: The lazy loading in EclipseLink JPA does not cause this issue:
User
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;
}
}
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;
}
}
Demo
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);
}
}
Output
You can see from the output that the address value is being lazily loaded since the field is null before the marshal and populated afterwards:
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
这篇关于(moxy)jaxb编组和hibernate代理对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!