Home > Back-end >  Mapping DTO with circular self dependency
Mapping DTO with circular self dependency

Time:01-31

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

  1. Select the actual user from repository – userRepository.findById(userId)
  2. Select followers – userFollowsRepository.findByFollowsUserId(userId)
  3. Select following – userFollowsRepository.findByUserId(userId)

It is a good practice to try and avoid bidirectional in entities relationships entirely if possible.

  • Related