I was getting the error “Could not write JSON: Infinite recursion” when trying to access the endpoint http://localhost:8080/categoryOfPermissions. I’ve researched and found various solutions here (@JsonManagedReference / @JsonBackReference
, @JsonIgnore
, @JsonIdentityInfo
), but none of them seemed to work. Finally, I found an answer stating that it was necessary to change from Set to List, in order for the @JsonIdentityInfo
solution to work. I tested it an it really starts to work after changing from Set to List.
I thought that it was strange, but I found out something even stranger: after changing from Set to List, I removed the @JsonIdentityInfo
annotations and everything continued to work. In other words, all that I really needed to do was changing from Set to List to get rid of the exception. Nothing else. No need of any of the solutions :@JsonManagedReference / @JsonBackReference
, @JsonIgnore
, @JsonIdentityInfo
.
Below is the code that was producing the exception. All I had to do was changing private Set<Permission> permission
to private List<Permission> permission
.
I would like to know why, especially because I would prefer to use Set, in order to avoid Hibernate to use the “Bags” paradigm (which may cause some undesirable behaviors).
Permission.java :
@Entity @Data public class Permission{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @NotBlank private String name; @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "category_of_permission_id") private CategoryOfPermission categoryOfPermission; }
CategoryOfPermission.java :
@Entity @Data public class CategoryOfPermission{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @NotBlank private String name; @NotNull @OneToMany(mappedBy = "categoryOfPermission", fetch=FetchType.LAZY) private Set<Permission> permission; }
CategoryOfPermissionRepo.java :
public interface CategoryOfPermissionRepo extends CrudRepository<CategoryOfPermission, Integer>{ }
Advertisement
Answer
It is because Set
in Java
uses equals
contract to determine whether two objects are the same or not, and the way equals
method in Permission
class is implemented(using lombok
) causes infinite recursion.
it is part of the generated code for equals
method in Permission
Object this$categoryOfPermission = this.getCategoryOfPermission(); Object other$categoryOfPermission = other.getCategoryOfPermission(); if (this$categoryOfPermission == null) { if (other$categoryOfPermission != null) { return false; } } else if (!this$categoryOfPermission.equals(other$categoryOfPermission)) { return false; }
and it is the generated code for the CategoryOfPermission
class
public boolean equals(final Object o) { if (o == this) { return true; } else if (!(o instanceof CategoryOfPermission)) { return false; } else { CategoryOfPermission other = (CategoryOfPermission)o; if (!other.canEqual(this)) { return false; } else { Object this$id = this.getId(); Object other$id = other.getId(); if (this$id == null) { if (other$id != null) { return false; } } else if (!this$id.equals(other$id)) { return false; } Object this$permission = this.getPermission(); Object other$permission = other.getPermission(); if (this$permission == null) { if (other$permission != null) { return false; } } else if (!this$permission.equals(other$permission)) { return false; } return true; } } }
as you see Permission
class calls equals
method of CategoryOfPermission
class and CategoryOfPermission
calls equals
method of Permision
class which finally causes stackoverflow problem!.