I have a JAX-RS webservice that makes use of JPA entity classes. I have a resource class like this:
@Path("/entity")
public class MyEntityResource
{
@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/{entity}")
public MyEntity getMyEntity(@PathParam("entity") String entity)
{
log.debug("Entering getMyEntity with param: " + entity);
MyEntity entityObject = genericService.find(MyEntity.class, entity);
if (entityObject == null)
{
log.debug("Entity not found.");
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
log.debug("Exiting getMyEntity");
return entityObject;
}
}
When I run the service and do a get call on the entity, I get this error:
SEVERE: The response of the WebApplicationException cannot be utilized as the response is already committed. Re-throwing to the HTTP container
javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException]
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:151)
...
<snip>
...
Caused by: javax.xml.bind.MarshalException
- with linked exception:
[Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Except开发者_如何学JAVAion: java.lang.NullPointerException]
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:271)
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:171)
at com.sun.jersey.core.provider.jaxb.AbstractRootElementProvider.writeTo(AbstractRootElementProvider.java:149)
... 32 more
Caused by: Exception [EclipseLink-25003] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred marshalling the object
Internal Exception: java.lang.NullPointerException
at org.eclipse.persistence.exceptions.XMLMarshalException.marshalException(XMLMarshalException.java:76)
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:502)
at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:269)
... 34 more
Caused by: java.lang.NullPointerException
at org.eclipse.persistence.exceptions.ConversionException.couldNotBeConverted(ConversionException.java:69)
...
<snip>
...
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:916)
at org.eclipse.persistence.oxm.XMLMarshaller.marshal(XMLMarshaller.java:468)
... 35 more
Nowhere in any of the stack traces are any of my classes referenced. Also, the "Exiting getMyEntity"
statement is logged before the exception.
I have no idea what is throwing the NPE or how to debug this.
Before this error, I was getting a [com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML:
from my JPA (EclipseLink) entity classes and I added the annotation MOXy @XmlInverseReference to my child class on the parent getter method.
Any thoughts on what could be throwing this exception?
We have had some offline discussion about this issue, and for the benefit of others that find this post below is the correct setup of using @XmlInverseReference at multiple levels:
Entity A
import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Set;
@Entity
@XmlRootElement
public class EntityA implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Set<EntityB> entityBs;
@Id
@XmlAttribute
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@OneToMany(mappedBy = "entityABean")
@XmlElement
public Set<EntityB> getEntityBs() {
return this.entityBs;
}
public void setEntityBs(Set<EntityB> entityBs) {
this.entityBs = entityBs;
}
}
Entity B
import java.io.Serializable;
import javax.persistence.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
import java.util.Set;
@Entity
@XmlRootElement
public class EntityB implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Set<EntityC> entityCs;
private EntityA entityABean;
@Id
@XmlAttribute
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@JoinColumn(name = "EntityA")
@XmlInverseReference(mappedBy = "entityBs")
public EntityA getEntityABean() {
return this.entityABean;
}
public void setEntityABean(EntityA entityABean) {
this.entityABean = entityABean;
}
@OneToMany(mappedBy = "entityBBean")
@XmlElement
public Set<EntityC> getEntityCs() {
return this.entityCs;
}
public void setEntityCs(Set<EntityC> entityCs) {
this.entityCs = entityCs;
}
}
Entity C
import java.io.Serializable;
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.XmlInverseReference;
@Entity
public class EntityC implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private EntityB entityBBean;
@Id
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@JoinColumn(name = "EntityB")
@XmlInverseReference(mappedBy = "entityCs")
public EntityB getEntityBBean() {
return this.entityBBean;
}
public void setEntityBBean(EntityB entityBBean) {
this.entityBBean = entityBBean;
}
}
Demo
import java.io.FileInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(EntityA.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
FileInputStream xml = new FileInputStream("src/test/jaxb/input.xml");
EntityA a = (EntityA) unmarshaller.unmarshal(xml);
for(EntityB b : a.getEntityBs()) {
System.out.println(b.getEntityABean());
for(EntityC c : b.getEntityCs()) {
System.out.println(c.getEntityBBean());
}
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(a, System.out);
}
}
Demo - Output
test.jaxb.EntityA@1292d26
test.jaxb.EntityB@196c1b0
test.jaxb.EntityB@196c1b0
test.jaxb.EntityA@1292d26
test.jaxb.EntityB@1e13d52
test.jaxb.EntityB@1e13d52
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<entityA>
<entityBs>
<entityCs/>
<entityCs/>
</entityBs>
<entityBs>
<entityCs/>
<entityCs/>
</entityBs>
</entityA>
This issue is also being handled on the EclipseLink forums, for more information see:
- http://www.eclipse.org/forums/index.php?t=msg&th=172544&start=0&
Below is another example of using @XmlInverseReference with a JPA model
- http://bdoughan.blogspot.com/2010/07/jpa-entities-to-xml-bidirectional.html
Did you include a jaxb.properties file in with your entity classes?
Check out Blaise's answer to this question: JAXB Mapping cyclic references to XML
Hope this helps. RG
精彩评论