Home > front end >  REST API Infinite loop
REST API Infinite loop

Time:12-01

My API shows me infinite loop for adress field

enter image description here

When I insert @JsonIgnore, @JsonManagedReference or @JsonBackReference I can clearly see one result as it should be, but than i don't have nested address fields.

enter image description here

What should I do to have also that address fields but one result?

These are my main entities:

1.Property

package com.realestate.petfriendly.entity;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Data;

@Entity
@Data
@Table(name = "property")
public class Property {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_property")
    private int id_property;
    @Column(name = "title")
    private String title;
    @Column(name = "type")
    private String type;
    @Column(name = "room")
    private String room;
    @Column(name = "price")
    private double price;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id_address")
//  @JsonBackReference
    private Address address;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id_user")
//  @JsonBackReference
    private User user;

} 
  1. User
package com.realestate.petfriendly.entity;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@Table(name = "user")
class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_user")
    private int id_user;
    @Column(name = "username")
    private String username;
    @Column(name = "name")
    private String name;
    @Column(name = "lastname")
    private String lastname;
    @Column(name = "phone")
    private String phone;
    @Column(name = "notes")
    private String notes;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "user_address_id_user_address")
//  @JsonManagedReference
    private UserAddress userAddress;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
//  @JsonManagedReference
    private List<Property> property = new ArrayList<>();

}
  1. Address
package com.realestate.petfriendly.entity;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@Table(name="address")
class Address{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_address")
    private int id_address;
    @Column(name = "city")
    private String city;
    @Column(name = "municipality")
    private String municipality;
    @Column(name = "place")
    private String place;
    @Column(name = "street")
    private String street;
    @Column(name = "house_number")
    private double house_number;
    
    @OneToOne(mappedBy = "address")
//  @JsonManagedReference
    private Property property;
} 

CodePudding user response:

You have circular dependency between Property and Address class. In order to block infinite JSON serialization loop you can add @JsonIgnore annotation on one side of related properties

CodePudding user response:

You actually have the solution to your problem in your code, but the key annotations are commented-out and in the wrong places (according to your requirements). One of the ways to tackle this is by using @JsonManagedReference and @JsonBackReference as follows:

@Entity
@Data
@Table(name = "property")
public class Property {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_property")
    private int id_property;
    @Column(name = "title")
    private String title;
    @Column(name = "type")
    private String type;
    @Column(name = "room")
    private String room;
    @Column(name = "price")
    private double price;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id_address")
    @JsonManagedReference
    private Address address;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "user_id_user")
    @JsonBackReference
    private User user;
}
@Entity
@Getter
@Setter
@Table(name = "user")
class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_user")
    private int id_user;
    @Column(name = "username")
    private String username;
    @Column(name = "name")
    private String name;
    @Column(name = "lastname")
    private String lastname;
    @Column(name = "phone")
    private String phone;
    @Column(name = "notes")
    private String notes;

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "user_address_id_user_address")
    private UserAddress userAddress;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "user")
    @JsonManagedReference
    private List<Property> property = new ArrayList<>();
}
@Entity
@Getter
@Setter
@Table(name="address")
class Address{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_address")
    private int id_address;
    @Column(name = "city")
    private String city;
    @Column(name = "municipality")
    private String municipality;
    @Column(name = "place")
    private String place;
    @Column(name = "street")
    private String street;
    @Column(name = "house_number")
    private double house_number;
    
    @OneToOne(mappedBy = "address")
    @JsonBackReference
    private Property property;
}

Keep in mind the following:

  • @JsonManagedReference is the forward part of the relationship: the one that gets serialized normally.
  • @JsonBackReference is the back part of the relationship: it will be omitted from serialization.

If you want to have a reference to the back part of the relationship, you can use @JsonIdentityInfo as follows:

@Entity
@Data
@Table(name = "property")
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id_property")
public class Property {
   (...)
}
@Entity
@Getter
@Setter
@Table(name = "user")
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id_user")
class User {
   (...)
}
@Entity
@Getter
@Setter
@Table(name="address")
@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class, 
  property = "id_address")
class Address{
   (...)
}

You can read more about these and other techniques in the following online resource: https://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion.

  • Related