hibernate - (moxy) jaxb 编码(marshal)处理和 hibernate 代理对象

标签 hibernate proxy jaxb lazy-loading moxy

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

    关于hibernate - (moxy) jaxb 编码(marshal)处理和 hibernate 代理对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6838323/

    相关文章:

    hibernate - 如何使用 hibernate 条件仅返回对象的一个​​元素而不是整个对象?

    ubuntu - 使用其他网络接口(interface)进行 apt

    docker - goproxy 背后的 gPRC 返回证书错误,在没有代理的情况下工作正常

    java - JAX-WS 服务中根元素的属性

    java - Java/Kotlin 中 XML 解码为对象

    java - Hibernate 抛出 SQLException "id field does not have a default value"

    hibernate - Hibernate标准中的关联大小

    java - HQL 查询中的语法错误 "unexpected token"

    c# - 测试代理设置的首选或可接受的方法是什么?

    jaxb - org.apache.camel.InvalidPayloadException : No body available of type error thrown while unMarshalling Jaxb Object