(moxy)jaxb编组和hibernate代理对象 [英] (moxy) jaxb marshaling and hibernate proxy objects

查看:133
本文介绍了(moxy)jaxb编组和hibernate代理对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在过去的几天里,我试图使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆