在过去的几天里,我尝试使用 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 实例化了通过对用户调用 getAddress() 获得的地址的代理对象。然后,当 JAXB 尝试编码(marshal)对象时,它无法发现它实际上是它试图编码(marshal)的 CoolAddress,这导致 CoolAddress 中的属性未被编码(marshal)。

我已经用谷歌搜索/考虑了以下可能的解决方案:
  • 以某种方式从 JAXB 获得回调,允许我用另一个对象替换正在编码的对象。这将允许我从代理获取真实对象。
  • 触摸模型中的所有对象,这将使 hibernate 获取真实的对象。除了手动运行所有非 transient 属性之外,我找不到任何聪明的方法来执行此操作,这很乏味。
  • 将 hibernate 设置为在我正在编码模型的 session 中使用预先获取。

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

    请您参考如下方法:

    要解决此 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 编码器,您也可以这样做,请参阅以下示例:
  • Using JAXB to cross reference XmlIDs from two XML files


  • 使用 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); 
        } 
     
    } 
    

    输出

    您可以从输出中看到地址值被延迟加载,因为该字段在 marshal 之前为 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 
    


    评论关闭
    IT干货网

    微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!