Could not write JSON: Infinite recursion

Estou recebendo o erro de recursão do StackOverflow quando executo a consulta no Postman ou no navegador.

O erro é .w.s.m.s.DefaultHandlerExceptionResolver : Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

@Entity
public class Product implements Serializable {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;
   @NotNull
   private String title;
   @NotNull
   private String description;
   @NotNull
   private double price;
   @OneToOne(fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name = "category_id", nullable = false)
   private Category category;
   private boolean isSealed;
   @OneToOne(fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name = "currency_id", nullable = false)
   private Currency currency;
   @OneToOne(fetch = FetchType.LAZY, optional = false)
   @JoinColumn(name = "user_id", nullable = false)
   private User user;
   @Nullable
   @OneToMany(mappedBy = "product",
   cascade = CascadeType.ALL, orphanRemoval = true)
   private List<Images> images;
   private Date createdDate = new Date();
   @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL,
   mappedBy = "product")
   private View view;
   @OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL)
   @JoinColumn(name="type_id")
   private Type type;
   private Long viewCount;
}

@Entity
public class User implements UserDetails, Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @NotEmpty
    private String fullName;
    @NotEmpty
    @Email
    @Column(unique = true)
    private String email;
    @NotNull
    @Column(unique = true)
    private int phoneNumber;
    @NotEmpty
    @Size(min = 5)
    private String password;
    private Date createAt = new Date();
    @Nullable
    private String picPath;
    @Nullable
    private String token;
    @ManyToMany
    @JoinTable(name = "user_roles", joinColumns = {@JoinColumn(
            name = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id")})
    private List<Role> roles;
    @OneToOne(fetch = FetchType.LAZY,
            cascade = CascadeType.ALL,
            mappedBy = "user")
    private Product product;
    @OneToOne(fetch = FetchType.LAZY,
    cascade = CascadeType.ALL,
    mappedBy = "user")
    private View view;
}

@Entity
public class Category implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String imagePath;
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY,
    mappedBy = "category")
    private Product product;
      @OneToMany(mappedBy = "category", fetch = FetchType.LAZY,
            cascade = CascadeType.ALL)
    private Set<Brand> brands;
}

@Entity
public class Images{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String imagePath;
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product_id")
    private Product product;
}

@Entity
public class Currency{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String code;
    private String currency;
    private String region_country;
    @OneToOne(mappedBy = "currency", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Product product;
}

@Entity
public class Type {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @NotNull
    private String name;
    @OneToOne(fetch = FetchType.LAZY,
            cascade = CascadeType.ALL,
            mappedBy = "type")
    private Product product;
}

@Entity
public class View {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    @OneToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "product_id", nullable = false)
    private Product product;
}

@Entity
public class Brand {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "category_id", nullable = false)
    private Category category;
}

@Entity
public class Role implements GrantedAuthority{
    @Id
    private String role;
    @ManyToMany(mappedBy = "roles")
    private List<User> users;
}

Isso é devido você estar usando relacionamento bidirecional. Coloque @JsonIgnore em um dos lados, onde não for necessário estar no json.

Ideal mesmo é trabalhar com DTOs enxutos de acordo com o que cada funcionalidade precisar, sem essa mistura de responsabilidades, onde o json fica pesado por ter uma estrutura genérica.

1 curtida

Quando acrescento o @JsonIgnore entre a classe Product e Category recebo o seguinte erro ::::

There was an unexpected error (type=Internal Server Error, status=500).

Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->mz.co.zonal.models.Product[“category”]->mz.co.zonal.models.Category$HibernateProxy$mA85i8U9[“hibernateLazyInitializer”])

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->mz.co.zonal.models.Product[“category”]->mz.co.zonal.models.Category$HibernateProxy$mA85i8U9[“hibernateLazyInitializer”]

Uma dica: Crie objetos exclusivos para retornar os dados que deseja em vez de retornar a entidade completa (e não polui seu dominio com o uso de @JsonIgnore). Ex.:

public class ProductResource {
	
	@JsonProperty Xxx xxx;
	@JsonProperty Yyy yyy;
	@JsonProperty Zzz zzz;
	
	ProductResource(Product product) {
		// faça as atribuições necessárias no construtor
		this.xxx = product.xxx;
		this.zzz = product.yyy;
		this.zzz = product.zzz;
	}
}

obs.: Coloquei @JsonProperty pois vc está usando o jackson e, com isso, não precisa criar os getters dos atributos.