I have an Entity as follows:
@XmlRootElement
@Entity
@Table(name="CATEGORY")
@Access(AccessType.FIELD)
@Cacheable
@NamedQueries({
@NamedQuery(name="category.countAllDeleted", query="SELECT COUNT(c) FROM Category c WHERE c.deletionTimestamp IS NOT NULL"),
@NamedQuery(name="category.findAllNonDeleted", query="SELECT c from Category c WHERE c.deletionTimestamp IS NULL"),
@NamedQuery(name="category.findByCategoryName", query="SELECT c FROM Category c JOIN c.descriptions cd WHERE LOWER(TRIM(cd.name)) LIKE ?1")
})
public class Category extends AbstractSoftDeleteAuditableEntity<Integer> implements za.co.sindi.persistence.entity.Entity<Integer>, Serializable {
/**
*
*/
private static final long serialVersionUID = 4600301568861226295L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="CATEGORY_ID", nullable=false)
private int id;
@ManyToOne
@JoinColumn(name="PARENT_CATEGORY_ID")
private Category parent;
@OneToMany(cascade= CascadeType.ALL, mappedBy="category")
private List<CategoryDescription> descriptions;
public void addDescription(CategoryDescription description) {
if (description != null) {
if (descriptions == null) {
descriptions = new ArrayList<CategoryDescription>();
}
descriptions.add(description);
}
}
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#getId()
*/
public Integer getId() {
// TODO Auto-generated method stub
return id;
}
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
*/
public void setId(Integer id) {
// TODO Auto-generated method stub
this.id = (id == null) ? 0 : id;
}
/**
* @return the parent
*/
public Category getParent() {
return parent;
}
/**
* @param parent the parent to set
*/
public void setParent(Category parent) {
this.parent = parent;
}
/**
* @return the descriptions
*/
public List<CategoryDescription> getDescriptions() {
return descriptions;
}
/**
* @param descriptions the descriptions to set
*/
public void setDescriptions(List<CategoryDescription> descriptions) {
this.descriptions = descriptions;
}
}
AND:
@XmlRootElement
@Entity
@Table(name="CATEGORY_DESCRIPTION")
@Access(AccessType.FIELD)
@Cacheable
public class CategoryDescription extends AbstractModifiableAuditableEntity<CategoryDescriptionKey> implements za.co.sindi.persistence.entity.Entity<CategoryDescriptionKey>, Serializable {
/**
*
*/
private static final long serialVersionUID = 4506134647012663247L;
@EmbeddedId
private CategoryDescriptionKey id;
@MapsId("categoryId")
@ManyToOne/*(fetch=FetchType.LAZY)*/
@JoinColumn(name="CATEGORY_ID", insertable=false, updatable=false, nullable=false)
private Category category;
@MapsId("languageCode")
@ManyToOne/*(fetch=FetchType.LAZY)*/
@JoinColumn(name="LANGUAGE_CODE", insertable=false, updatable=false, nullable=false)
private Language language;
@Column(name="CATEGORY_NAME", nullable=false)
private String name;
@Column(name="DESCRIPTION_PLAINTEXT", nullable=false)
private String descriptionPlainText;
@Column(name="DESCRIPTION_MARKDOWN", nullable=false)
private String descriptionMarkdown;
@Column(name="DESCRIPTION_HTML", nullable=false)
private String descriptionHtml;
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#getId()
*/
public CategoryDescriptionKey getId() {
// TODO Auto-generated method stub
return id;
}
/* (non-Javadoc)
* @see za.co.sindi.entity.IDBasedEntity#setId(java.io.Serializable)
*/
public void setId(CategoryDescriptionKey id) {
// TODO Auto-generated method stub
this.id = id;
}
/**
* @return the category
*/
public Category getCategory() {
return category;
}
/**
* @param category the category to set
*/
public void setCategory(Category category) {
this.category = category;
}
/**
* @return the language
*/
public Language getLanguage() {
return language;
}
/**
* @param language the language to set
*/
public void setLanguage(Language language) {
this.language = language;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the descriptionPlainText
*/
public String getDescriptionPlainText() {
return descriptionPlainText;
}
/**
* @param descriptionPlainText the descriptionPlainText to set
*/
public void setDescriptionPlainText(String descriptionPlainText) {
this.descriptionPlainText = descriptionPlainText;
}
/**
* @return the descriptionMarkdown
*/
public String getDescriptionMarkdown() {
return descriptionMarkdown;
}
/**
* @param descriptionMarkdown the descriptionMarkdown to set
*/
public void setDescriptionMarkdown(String descriptionMarkdown) {
this.descriptionMarkdown = descriptionMarkdown;
}
/**
* @return the descriptionHtml
*/
public String getDescriptionHtml() {
return descriptionHtml;
}
/**
* @param descriptionHtml the descriptionHtml to set
*/
public void setDescriptionHtml(String descriptionHtml) {
this.descriptionHtml = descriptionHtml;
}
}
When returning a Collection<Category>
using JAX-RS and deploying on JBoss Wildfly 8.2.0-Final, I get the following stacktrace:
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: za.co.sindi.unsteve.persistence.entity.Category["descriptions"]->org.hibernate.collection.internal.PersistentBag[0]->za.co.sindi.unsteve.persistence.entity.CategoryDescription["category"]->za.co.sindi.unsteve.persistence.entity.Category["descriptions"]->
There are answers answered in questions like this question, that requires to use Jackson specific annotations. My project's requirement is to strictly stick to Java EE specific frameworks. Is there a solution that we can use to prevent infinite recursion without using Jackson annotations? If not, can we create a config file (XML file, etc.), that Jackson can use in place of annotations? The reason for this is that the application must not only be bound to Wildfly specific libraries.