Infinite recursion (Stackoverflowerror): Error when listing products with categories and unit of measures

Asked

Viewed 5,651 times

7

Good afternoon! I am trying to list the registered products, but the error happens below:

Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

Entities:

Products

@Entity
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Product {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotEmpty
@Length(max = 180)
private String name;

@NotEmpty
@Length(max = 180)
private String description;

@NotNull
private double unitPrice;

@NotNull
private Long amount;

@NotNull
@Temporal(TemporalType.TIMESTAMP)
@JsonFormat(pattern="dd-MM-yyyy")
private Date registerDate;

@NotNull
@Temporal(TemporalType.TIMESTAMP)
@JsonFormat(pattern="dd-MM-yyyy")
private Date alterationDate = new java.sql.Date(System.currentTimeMillis());

@NotNull
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
private Category category;

@NotNull
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
@JoinColumn(name = "unitMeasure_id", nullable = false)
private UnitMeasure unitMeasure;

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
@JoinColumn(name = "orders_id")
private Orders orders;

Product(){}

public Product(String name, String description, double unitPrice, Long amount, Date registerDate, Date alterationDate, Category category, UnitMeasure unitMeasure, Orders orders) {
    this.name = name;
    this.description = description;
    this.unitPrice = unitPrice;
    this.amount = amount;
    this.registerDate = registerDate;
    this.alterationDate = alterationDate;
    this.category = category;
    this.unitMeasure = unitMeasure;
    this.orders = orders;
}

public Long getId() {
    return id;
}
public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getDescription() {
    return description;
}
public void setDescription(String description) {
    this.description = description;
}

public double getUnitPrice() {
    return unitPrice;
}
public void setUnitPrice(double unitPrice) {
    this.unitPrice = unitPrice;
}

public Long getAmount() {
    return amount;
}

public void setAmount(Long amount) {
    this.amount = amount;
}

public Date getRegisterDate() {
    return registerDate;
}
public void setRegisterDate(Date registerDate) {
    this.registerDate = registerDate;
}

public Date getAlterationDate() {
    return alterationDate;
}
public void setAlterationDate(Date alterationDate) {
    this.alterationDate = alterationDate;
}

public Category getCategory() {
    return category;
}
public void setCategory(Category category) {
    this.category = category;
}

public Orders getOrders() {
    return orders;
}
public void setOrders(Orders orders) {
    this.orders = orders;
}

public UnitMeasure getUnitMeasure() {
    return unitMeasure;
}
public void setUnitMeasure(UnitMeasure unitMeasure) {
    this.unitMeasure = unitMeasure;
}

}

Category

@Entity
@JsonAutoDetect
public class Category {


@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotEmpty
@Length(max = 180)
private String name;

@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category")
private List<Product> products  = new ArrayList<>();

public Category() {}

public Long getId() {
    return id;
}
public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

public List<Product> getProducts() {
    List<Product> productSecureList = Collections.unmodifiableList(this.products);
    return productSecureList;
}
public void setProducts(Product products) {
    this.products.add(products);
    products.setCategory(this);
}

}

Unit of Measure:

@Entity
public class UnitMeasure {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotEmpty
@Length(max = 50)
private String name;

@NotEmpty
@Length(max = 15)
private String symbol;

@JsonBackReference
@OneToMany
private List<Product> products = new ArrayList<>();

UnitMeasure() {}

public UnitMeasure(String description, List<Product> products) {
    this.name = description;
    this.products = products;
}

public Long getId() {
    return id;
}
public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

public String getSymbol() {
    return symbol;
}

public void setSymbol(String symbol) {
    this.symbol = symbol;
}

public List<Product> getProducts() {
    List<Product> productSecureList = Collections.unmodifiableList(this.products);
    return productSecureList;
}
public void setProducts(Product products) {
    this.products.add(products);
    products.setUnitMeasure(this);
}

}

inserir a descrição da imagem aqui

1 answer

15


The problem is that you have a bi-directional relationship between Product and Category. When Jackson tries to serialize a product, he arrives in the category property, then tries to serialize category and has a product, staying in infinite recursive loop, as the message.

There are some solutions to this:

  1. Use the annotations @JsonBackReference and @JsonManagedReference
public class Product {
    @NotNull
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id", nullable = false)
    @JsonBackReference
    private Category category;
}

public class Category {
    @OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "category")
    @JsonManagedReference
    private List<Product> products  = new ArrayList<>();
}
  1. Using the notation @JsonIdentityInfo in the classes
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class Product {
    ...
}

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id")
public class Category {
    ...
}
  1. Using a simple @JsonIgnore property of one of the classes.

    The same problem should occur between Product and Unitmeasure

  • Sorry, but I’m not able to format properly as code for better reading.

  • Good evening! Thank you very much for your reply! I managed to solve using @Jsonignore in getters methods, but I will test the other ways presented by you. Thanks!!!

  • Hello L. Albano following your reply I used the second option given by you and now I can register the products, categories and units of good measures. But today I went to test the product update and is giving an error(I will edit my question with the error that occurs when I get home from college today). Another problem I would like to solve is that when listing the product json ignores the product category and unit of measurement, and I would like to have that information on my front when listing a product. How can I fix this?

  • Saved my life <3

Browser other questions tagged

You are not signed in. Login or sign up in order to post.