(moxy) jaxb marshaling and hibernate proxy objects

后端 未结 1 1268
死守一世寂寞
死守一世寂寞 2020-12-20 21:03

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

相关标签:
1条回答
  • 2020-12-20 21:24

    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:

    • Using JAXB to cross reference XmlIDs from two XML files

    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
    
    0 讨论(0)
提交回复
热议问题