0

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;
} 
João Dias
  • 16,277
  • 6
  • 33
  • 45
Developer
  • 35
  • 12
  • Does this answer your question? [Infinite Recursion with Jackson JSON and Hibernate JPA issue](https://stackoverflow.com/questions/3325387/infinite-recursion-with-jackson-json-and-hibernate-jpa-issue) – M. Dudek Nov 30 '21 at 22:44
  • @M.Dudek thanks. I have already seen this and used JsonManaged and back references, but I didn't realize I should use them in the same entity. Instead, I put one reference in one entity and another one in related entity and used them just like OneToMany and ManyToOne relations :D – Developer Dec 01 '21 at 11:48

2 Answers2

2

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.

João Dias
  • 16,277
  • 6
  • 33
  • 45
  • Thanks a lot. This really helps me. I didn't realize I should use them in the same entity. I put them just like OneToMany and ManyToOne relations - one reference in one entity and the other one in another. Now I get it :D – Developer Dec 01 '21 at 11:54
  • Awesome! If my answer helped you consider upvoting it and even accepting it as the answer so that it can be ”closed” and others can benefit from it and easily understand which could be a possible solution for similar questions ;) Thanks! – João Dias Dec 01 '21 at 14:42
  • Of course. It is done :) – Developer Dec 02 '21 at 10:34
1

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

Francesco Genta
  • 333
  • 1
  • 8