I have a entity user with self dependency, follower and follow. When i Map this entity to DTO I have the problem of circular dependency. (The problem is only when I have to map because jpa/hibernate can't help me in this job). User.class:
public class User {
@Id
@GeneratedValue
private Long id;
@NotNull
@Column(name = "first_name")
private String firstName;
@NotNull
@Column(name = "last_name")
private String lastName;
@JsonBackReference
@ManyToMany(
cascade = CascadeType.ALL)
@JoinTable(name = "user_followers",
joinColumns = {@JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "follower_Id")})
private List<User> followers = new ArrayList<>();
@JsonIgnore
@ManyToMany(cascade = CascadeType.ALL,
mappedBy = "followers")
private List<User> following = new ArrayList<>();
UserMapper method in UserMapper:
public static UserResponse toUser(User user) {
UserResponse userResponse = new UserResponse();
userResponse.setId(user.getId());
userResponse.setFirstName(user.getFirstName());
userResponse.setLastName(user.getLastName());
userResponse.setFollowers(user.getFollowers().stream().map(UserMapper::toUser).toList());
userResponse.setFollowing(user.getFollowing().stream().map(UserMapper::toUser).toList());
return userResponse;
}
When i run the method toUser() I get stackOverFlowError exception caused by the infinite circular dependency. Any advise how to solve this?
CodePudding user response:
One way to resolve this is to model the 'follows' relationship as a separate entity:
@Table(name="user_followers")
public class Follows {
@Id
@GeneratedValue
private Long id;
@NotNull
@Column(name = "follower_Id")
private User follower;
@NotNull
@Column(name = "user_id")
private User user;
}
Then you could give your user two one-to-many lists of these entities:
public class User {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "user_id")
private List<Follows> followers;
@OneToMany(mappedBy = "follower_Id")
private List<Follows> following;
}
EDIT: instead of the id field in Follows you could use the user_id and follower_id as a composite primary key using @Embeddable. Omitted here for brevity. See here for more details: https://www.baeldung.com/jpa-many-to-many
CodePudding user response:
Since you already have a DTO of UserResponse
, you are on the right path towards a correct solution. My suggestion would be to avoid @ManyToMany
on an entity level, and manage followers on a service level.
This means you will have to split relation ManyToMany join column into a separate entity, such as UserFollowsEntity
with fields userId
and followsUserId
. Then remove followers
and following
lists from your User entity entirely.
Now when creating UserResponse
in a service, you will have to
- Select the actual user from repository –
userRepository.findById(userId)
- Select followers –
userFollowsRepository.findByFollowsUserId(userId)
- Select following –
userFollowsRepository.findByUserId(userId)
It is a good practice to try and avoid bidirectional in entities relationships entirely if possible.