JSONmanagedreference VS JSONbackreference

JsonManagedReference vs JsonBackReference

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

So they really depend on the direction of your relationship

public class User {
public int id;
public String name;

@JsonBackReference
public List<Item> userItems;
}

public class Item {
public int id;
public String itemName;

@JsonManagedReference
public User owner;
}

How to use @JsonManagedReference and @JsonBackReference for Many to Many relationship with best practice?

The @JsonManagedReference and @JsonBackReference annotations are used to handle circular references. They can be used to create a JSON structure in a bidirectional way. The @JsonManagedReference annotation is used on a child reference of the target POJO, which means it is a forward reference that includes during the serialization process whereas @JsonBackReference annotation is a backreference that omits during the serialization process, usually used in the corresponding child class.

Hence, The property annotated with @JsonBackReference here, which is the Team in the TeamUsers, won't be serialized. That is why when you try to get all the users having Team inside the TeamUsers, it won't work. Also, if it did, it would violate the purpose of the annotations that they're used for, recursive access mapping.

If you want to fetch data in either way, you should use @JsonIgnoreProperties instead of those two annotations. Change your entity classes as follows and you'll get your desired output.

In Team class, set @JsonIgnoreProperties("team") on the team_users field to ignore mapping team inside this field again to avoid recursive mapping. Change your Team class to:

public class Team {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "TEAM_ID")
private Integer id;

@OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
@JsonIgnoreProperties("team")
private Set<TeamUsers> team_users = new HashSet<>();

}

Similarly, in User class, set @JsonIgnoreProperties("user") on the team_users field to ignore mapping user inside this field again to avoid recursive mapping. Change your User class to:

public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
@JsonIgnoreProperties("user")
private Set<TeamUsers> team_users = new HashSet<>();
}

And finally, in TeamUsers class, set @JsonIgnoreProperties("team_users") on both the team and user field to ignore mapping team_users inside these field again to avoid recursive mapping. Change your TeamUsers class to:

public class TeamUsers {

@EmbeddedId
private TeamUserId id;

@ManyToOne
@MapsId("teamId")
@JoinColumn(name = "team_id")
@JsonIgnoreProperties("team_users")
private Team team;

@ManyToOne
@MapsId("userId")
@JoinColumn(name = "user_id")
@JsonIgnoreProperties("team_users")
private User user;

@Column(name = "active")
private int active;
}

Now you can fetch the data in either way without having recursive mapping.

Difference between @JsonIgnore and @JsonBackReference, @JsonManagedReference

Lets suppose we have

private class Player {
public int id;
public Info info;
}
private class Info {
public int id;
public Player parentPlayer;
}

// something like this:
Player player = new Player(1);
player.info = new Info(1, player);

Serialization

@JsonIgnore

private class Info {
public int id;
@JsonIgnore
public Player parentPlayer;
}

and @JsonManagedReference + @JsonBackReference

private class Player {
public int id;
@JsonManagedReference
public Info info;
}

private class Info {
public int id;
@JsonBackReference
public Player parentPlayer;
}

will produce same output. And output for demo case from above is: {"id":1,"info":{"id":1}}

Deserialization

Here is main difference, because deserialization with @JsonIgnore
will just set null to the field so in our example parentPlayer will be == null.

Sample Image

But with @JsonManagedReference + @JsonBackReference we will get Info referance there

Sample Image

How to swap @JsonBackReference and @JsonManagedReference based on which Entity is reference

If I understand you correctly the @JsonIgnoreProperties annotation should help you:

@JsonIgnoreProperties("site")
@OneToMany(mappedBy="site")
private List<Building> buildings;

@JsonIgnoreProperties("buildings")
@ManyToOne
private Site site;

Is there a way to get @JsonBackReference only working when the element is in a collection, but not when standalone?

If you want the references to be serialized from both sides(Book, Author) then jackson faces the issue with circular reference where it get's stuck in an endless loop of references when serializing one of those 2 objects.

The workaround was with @JsonManagedReference and @JsonBackReference where jackson ignored 1 side and serialized only the other side in order to avoid circular reference.

The solution to your problem (when you want to serialize both sides) is to create seperate DTO objects(AuthorDto, BookDto) and instead of returning from your controller a Author to be serialized you return an AuthorDto. Same with Book. Then circlular reference problem does not exist any more and both sides serialize the problematic references.

Using Set instead of List cause Could not write JSON: Infinite recursion exception

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!.



Related Topics



Leave a reply



Submit