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.
But with @JsonManagedReference
+ @JsonBackReference
we will get Info
referance there
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
How Can My Java Program Store Files Inside of Its .Jar File
Difference Between Getclass().Getclassloader().Getresource() and Getclass.Getresource()
Class.Getresource() Returns Null
Getting Database Connection in Pure JPA Setup
Why Main() Method Is Needed in Java Main Class
Spring @Autowired and @Qualifier
Browse for Image File and Display It Using Java Swing
Jtable How to Change Background Color
Converting Decimal to Binary Java
Java - Swing Listen an Action in a Text Field of a Form
Why Only 1 Public Class in Java File
Find Place for Dedicated Application Folder
Why Do We Need Copy Constructor and When Should We Use Copy Constructor in Java
Class Not Found with Ant, Ivy and Junit - Error in Build.Xml