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

2

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

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);
}

}

    
asked by anonymous 01.10.2017 / 19:32

1 answer

5

The problem is that you have a two-way relationship between Product and Category. When Jackson tries to serialize a product, it arrives in category property, then tries to serialize category and has a product, getting into infinite recursive loop, as per the message.

There are some solutions to this:

  • Use 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<>();
    }
    
  • Using the @JsonIdentityInfo notation in the classes
  • @JsonIdentityInfo(
      generator = ObjectIdGenerators.PropertyGenerator.class, 
      property = "id")
    public class Product {
        ...
    }
    
    @JsonIdentityInfo(
      generator = ObjectIdGenerators.PropertyGenerator.class, 
      property = "id")
    public class Category {
        ...
    }
    
  • Using a simple @JsonIgnore in one of the properties of one of the classes.

    The same problem should occur between Product and UnitMeasure

  • 02.10.2017 / 06:09