I have a couple one to many relationships in spring between my classes. And when i try to login I get the error, it is like infinite recursion, here is the whole error message.
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:149) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:112) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704) ~[jackson-databind-2.8.10.jar:2.8.10] at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:689) ~[jackson-databind-2.8.10.jar:2.8.10] I have 5 classes that are connected with each other. Here they are:
@Entity public class AppUser implements UserDetails { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @Column(unique = true) private String username; private String password; @ElementCollection(fetch = FetchType.EAGER) private List<String> roles = new ArrayList<>(); @ManyToOne @JoinColumn(name = "apartmen_id") @JsonIgnoreProperties(value = {"apartmen_tenats"}, allowSetters=true) private Apartmen apartmen; // apartmen in which he lives @ManyToOne @JoinColumn(name = "institution_id") @JsonIgnoreProperties(value = {"workers"}, allowSetters=true) private Institution institution; // institution in which he works @OneToMany(mappedBy = "worker") @JsonIgnoreProperties(value = {"worker"}, allowSetters = true) private Set<Failure> failures; // Kvarovi na kojima je radio } The second class:
@Entity public class Failure implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String description; // Opis kvara @Temporal(TemporalType.TIMESTAMP) private Date dateCreated; // Datum i vreme kada je kvar kreiran @Temporal(TemporalType.TIMESTAMP) private Date dateSolved; // Datum i vreme kada je kvar popravljen private boolean solved; // Da li je kvar popravljen @ManyToOne @JoinColumn(name = "app_user_id") @JsonIgnoreProperties(value = {"failures"}, allowSetters=true) private AppUser worker; // Radnik koji je zaduzen za kvar @ManyToOne @JoinColumn(name = "institution_id") @JsonIgnoreProperties(value = {"failures"}, allowSetters=true) private Institution institution; // Institucija kojoj je kvar prijavljen @ManyToOne @JoinColumn(name = "building_id") @JsonIgnoreProperties(value = {"failures"}, allowSetters=true) private Building building; // Zgrada u kojoj je kvar nastao } The third class:
@Entity public class Building implements Serializable{ /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String location; private String owner; // Vlasnik zgrade private int numberOfApartments; private int numberOfAparartmentsWithTenats; // Broj stanova koji su naseljeni private boolean hasPresident; // Oznacava da li zgrada ima predsednika skupstine stanara @OneToMany(mappedBy = "apartmenBuilding") @JsonIgnoreProperties(value = {"apartmenBuilding"}, allowSetters = true) private Set<Apartmen> apartments; // stanovi u zgradi @ManyToMany(mappedBy = "buildings") @JsonIgnoreProperties(value = {"buildings"}, allowSetters = true) private Set<Institution> institutions; @OneToMany(mappedBy = "building", fetch = FetchType.EAGER) @JsonIgnoreProperties(value = {"building"}, allowSetters = true) private List<Failure> failures; // Kvarovi koji su nastali u zgradi } The fourth class:
@Entity public class Institution { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String location; private String director; private String email; private String contactPhone; private String webSiteUrl; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE) @JoinTable(name = "institution_building", joinColumns = @JoinColumn(name = "institution_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "building_id", referencedColumnName = "id")) @JsonIgnoreProperties(value = {"institutions"}, allowSetters=true) private Set<Building> buildings; // Buildings which this institution is maintaining @OneToMany(mappedBy = "institution") @JsonIgnoreProperties(value = {"institution"}, allowSetters = true) private Set<AppUser> workers; @OneToMany(mappedBy = "institution", fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JsonIgnoreProperties(value = {"institution"}, allowSetters = true) private Set<Failure> failures; // Kvarovi na kojima je radila institucija } And the last class is:
@Entity public class Apartmen implements Serializable { /** * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; private String location; private String owner; private int numberOfTenats; private boolean hasApartmentBuilding; private boolean hasOwner; @ManyToOne @JoinColumn(name = "building_id") @JsonIgnoreProperties(value = {"apartments"}, allowSetters=true) private Building apartmenBuilding; @OneToMany(mappedBy = "apartmen") @JsonIgnoreProperties(value = {"apartmen"}, allowSetters = true) private Set<AppUser> apartmen_tenats; } I don't know why it happens, but maybe it is because of the relationship with the class Failure. I think that may be the case because that class has a relationship with every other class in my code. If that is the case, than can you please show me why is it happening and how to do it properly.
Thank you in advance.
12 Answers
@JsonIgnoreProperties is a class level annotation and it expects the exact field names in the class to ignore.
The best way is to replace all your @JsonIgnoreProperties with simply
@JsonIgnore. i.e.
@ManyToOne @JoinColumn(name = "apartmen_id") @JsonIgnoreProperties(value = {"apartmen_tenats"}, allowSetters=true) private Apartmen apartmen; Change this to
@ManyToOne @JoinColumn(name = "apartmen_id") @JsonIgnore private Apartmen apartmen; Make similar changes for all such occurrences of @JsonIgnoreProperties. This should work.
While converting Entity to JSON object it may cause this kind of issue Due to Cyclic object setting like (Parent has Child Joining) and (Child has Parent Joining),so Please don't Use Entity Pojo directly,it may cause SECURITY Issue also,and modification like adding @JsonIgnoreProperies in Entity also is not a good coding practice. Instead of that use to create DTO(Data Transfer Object) ,that means create the POJO class without JPA annotations and Use the ModelMapper () dependency ,it will automatically converts(Setting the entity variables to DTO variables) Entity to DTO Object.
Parent Entity Class
@Entity public class ParentEntity{ @Id private int parentId; @OneToOne(mappedBy = "parentObj") private ChildEntity childObj; //Getters Setters } Child Entity Class
@Entity public class ChildEntity{ @Id private ChildId; @OneToOne @JoinColumn("parentId") private ParentEntity parentObj; //Getters Setters } Parent DTO Class
@Data //Lombok Dependency public class ParentDto{ private int parentId; private ChildDto childDtoObj; //Getters Setters } Child DTO Class
@Data //Lombok Dependency public class ChildDto{ private ChildId; @JsonIgnore //In DTO u can make Modifications like adding JsonIgnore private ParentEntity parentObj; //Getters Setters } Once created DTO Class no need to do the Boiler Plate Code Like Entity Object to DTO Object setting.Let ModelMapper Object will do.Example code is Here
Service Class
@Service public class MainService{ @Autowired private ModelMapper mapper; public ParentDto doLogic() { ParentEntity jpaEntity = repoParent.findAll(); //Entity Object //Entity Object has Converted to DTO Object ParentDto parentDtoObj = mapper.map(jpaEntity,ParentDto.class); return parentDtoObj ; } }