PEIRAN LIU
PEIRAN LIU

Reputation: 157

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

I will be much apprecaite for you to explain my questions.

I have 2 entity and 1 bridge table entity,

Let's say they are Team, User and TeamUser.

TeamEntity:

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

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

UserEntity:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@JsonBackReference
@OneToMany(mappedBy = "user")
private Set<TeamUsers> team_users = new HashSet<>();

TeamUserEntity(bridge table): @EmbeddedId private TeamUsersId id;

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

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

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

As you can see I used @JsonManagedReference and @JsonBackReference to telling the program the direction for the query of the Entity and avoid infinite recrusive.

Now if I run get repo.findAll() on Team CRUDrepository I will get all Team object, and within the content I will get all bridge table data and it also include User details information.

But Let's say sometimes if I want to query the data in oppisite way, I want to get All User information and the object should contain all Team information, looks like the annotation @JsonManagedReference and @JsonBackReference block the result.

In real world development, how should we manage here?

Upvotes: 3

Views: 7775

Answers (1)

devReddit
devReddit

Reputation: 2947

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.

Upvotes: 13

Related Questions