java 在对象图中检测到循环。这将导致无限深的 XML
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17295370/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
A cycle is detected in the object graph. This will cause infinitely deep XML
提问by Prats
I have two DTO objects say A and B which are having getters and setters and are used to take data from the database. The problem is when I am calling A, B gets called and B again points itself to A and a cycle is created.
我有两个 DTO 对象说 A 和 B,它们有 getter 和 setter,用于从数据库中获取数据。问题是当我调用 A 时,B 被调用并且 B 再次将自己指向 A 并创建了一个循环。
I cannot ignore/hide the method which is creating the cycle. I need to take the whole data of A and B.
我不能忽略/隐藏创建循环的方法。我需要获取A和B的全部数据。
Is there any way to achieve it ?
有什么办法可以实现吗?
Please help
请帮忙
This is my code which is causing the problem. This is application DTO which is calling environment DTO
这是我的代码导致问题。这是正在调用环境 DTO 的应用程序 DTO
@OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
And this is environment DTO which is calling the application DTO
这是调用应用程序 DTO 的环境 DTO
@ManyToOne(targetEntity=ApplicationDTO.class )
@JoinColumn(name="fk_application_Id")
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
Here cycle is getting created
这里循环正在创建
This is my rest call which will give result in XML format and I think while creating XML cycle is getting created
这是我的休息调用,它将给出 XML 格式的结果,我认为在创建 XML 循环时正在创建
@GET
@Path("/get")
@Produces({MediaType.APPLICATION_XML})
public List<ApplicationDTO> getAllApplications(){
List<ApplicationDTO> allApplication = applicationService.getAllApplication();
return allApplication;
}
This is the Application DTO class
这是应用程序 DTO 类
@Entity
@Table(name="application")
@org.hibernate.annotations.GenericGenerator(
name ="test-increment-strategy",strategy = "increment")
@XmlRootElement
public class ApplicationDTO implements Serializable {
@XmlAttribute
public Long appTypeId;
private static final long serialVersionUID = -8027722210927935073L;
private Long applicationId;
private String applicationName;
private ApplicationTypeDTO applicationType;
private String applicationDescription;
private Integer owner;
private Integer createdBy;
private Integer assignedTo;
private Date createTime;
private Date modifiedTime;
private Set<EnvironmentDTO> environment;
@Id
@GeneratedValue(generator = "test-increment-strategy")
@Column(name = "applicationId")
public Long getApplicationId() {
return applicationId;
}
private void setApplicationId(Long applicationId) {
this.applicationId = applicationId;
}
@Column(name = "applicationName")
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
@ManyToOne(targetEntity=ApplicationTypeDTO.class
,fetch = FetchType.LAZY
)
@JoinColumn(name="applicationType")
public ApplicationTypeDTO getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationTypeDTO applicationType) {
this.applicationType = applicationType;
}
@Column(name = "description")
public String getApplicationDescription() {
return applicationDescription;
}
public void setApplicationDescription(String applicationDescription) {
this.applicationDescription = applicationDescription;
}
@Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
@Column(name = "createdBy")
public Integer getCreatedBy() {
return createdBy;
}
public void setCreatedBy(Integer createdBy) {
this.createdBy = createdBy;
}
@Column(name = "assignedTo")
public Integer getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(Integer assignedTo) {
this.assignedTo = assignedTo;
}
@Column(name = "createTime")
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Column(name = "modifiedTime")
public Date getModifiedTime() {
return modifiedTime;
}
public void setModifiedTime(Date modifiedTime) {
this.modifiedTime = modifiedTime;
}
@OneToMany(mappedBy="application", fetch=FetchType.LAZY
,cascade=CascadeType.ALL
)
public Set<EnvironmentDTO> getEnvironment() {
return environment;
}
public void setEnvironment(Set<EnvironmentDTO> environment) {
this.environment = environment;
}
This is the Environment DTO class
这是环境 DTO 类
@Entity
@Table(name="environment")
@org.hibernate.annotations.GenericGenerator(
name = "test-increment-strategy",
strategy = "increment")
@XmlRootElement
public class EnvironmentDTO implements Serializable {
@XmlAttribute
public Long envTypeId;
@XmlAttribute
public Long appId;
private static final long serialVersionUID = -2756426996796369998L;
private Long environmentId;
private String environmentName;
private EnvironmentTypeDTO environmentType;
private Integer owner;
private Date createTime;
private Set<InstanceDTO> instances;
private ApplicationDTO application;
@Id
@GeneratedValue(generator = "test-increment-strategy")
@Column(name = "envId")
public Long getEnvironmentId() {
return environmentId;
}
private void setEnvironmentId(Long environmentId) {
this.environmentId = environmentId;
}
@Column(name = "envName")
public String getEnvironmentName() {
return environmentName;
}
public void setEnvironmentName(String environmentName) {
this.environmentName = environmentName;
}
@ManyToOne(targetEntity=EnvironmentTypeDTO.class)
@JoinColumn(name = "envType")
public EnvironmentTypeDTO getEnvironmentType() {
return environmentType;
}
public void setEnvironmentType(EnvironmentTypeDTO environmentType) {
this.environmentType = environmentType;
}
@Column(name = "owner")
public Integer getOwner() {
return owner;
}
public void setOwner(Integer owner) {
this.owner = owner;
}
@Temporal(TemporalType.DATE)
@Column(name = "createTime")
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@OneToMany(mappedBy="environment", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
public Set<InstanceDTO> getInstances() {
return instances;
}
public void setInstances(Set<InstanceDTO> instances) {
this.instances = instances;
}
@ManyToOne(targetEntity=ApplicationDTO.class )
@JoinColumn(name="fk_application_Id")
//@XmlTransient
public ApplicationDTO getApplication() {
return application;
}
public void setApplication(ApplicationDTO application) {
this.application = application;
}
回答by Tom Anderson
Your object graph is cyclic. There is nothing intrinsically wrong with that, and it is a natural consequence of using JPA.
您的对象图是循环的。这没有本质上的错误,这是使用 JPA 的自然结果。
Your problem is not that your object graph is cyclic, but that you are encoding it in a format which cannot handle cycles. This isn't a Hibernate question, it's a JAXB question.
您的问题不是您的对象图是循环的,而是您以无法处理循环的格式对其进行编码。这不是 Hibernate 问题,而是 JAXB 问题。
My suggestion would be to stop JAXB from attempting to marshal the application
property of the EnvironmentDTO
class. Without that property the cyclic graph becomes a tree. You can do this by annotating that property with @XmlTransient
.
我的建议是阻止 JAXB 尝试编组该类的application
属性EnvironmentDTO
。没有这个属性,循环图就变成了一棵树。您可以通过使用 注释该属性来做到这一点@XmlTransient
。
(confession: i learned about this annotation by reading a blog post by Mr Doughan, which i came across after reading his answer to this question!)
(坦白:我是通过阅读Doughan 先生的博客文章了解到这个注释的,这是我在阅读他对这个问题的回答后发现的!)
回答by bdoughan
Note:I'm the EclipseLink JAXB (MOXy)lead and a member of the JAXB (JSR-222)expert group.
注意:我是EclipseLink JAXB (MOXy) 的负责人,也是JAXB (JSR-222)专家组的成员。
MOXy offers the @XmlInverseReference
extension to handle this use case. Below is an example of how to apply this mapping on two entities with a bidirectional relationship.
MOXy 提供了@XmlInverseReference
处理此用例的扩展。下面是如何将此映射应用于具有双向关系的两个实体的示例。
Customer
顾客
import javax.persistence.*;
@Entity
public class Customer {
@Id
private long id;
@OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
Address
地址
import javax.persistence.*;
import org.eclipse.persistence.oxm.annotations.*;
@Entity
public class Address implements Serializable {
@Id
private long id;
@OneToOne
@JoinColumn(name="ID")
@MapsId
@XmlInverseReference(mappedBy="address")
private Customer customer;
}
For More Information
想要查询更多的信息
回答by smftr
My advice is not exposing your JPA entity class to your webservices. You can create different POJO class and convert your JPA entity to the POJO. For example:
我的建议是不要将您的 JPA 实体类暴露给您的网络服务。您可以创建不同的 POJO 类并将您的 JPA 实体转换为 POJO。例如:
this is your JPA entity
这是您的 JPA 实体
import javax.persistence.*;
@Entity
public class Customer {
@Id
private long id;
@OneToOne(mappedBy="customer", cascade={CascadeType.ALL})
private Address address;
}
you should use this class for your webservices:
你应该为你的网络服务使用这个类:
public class CustomerModel{
private long id;
//you can call different WS to get the Address class, or combine to this model
public void setFromJpa(Customer customer){
this.id = customer.id;
}
}